Full Code of JoeanAmier/TikTokDownloader for AI

master d9c12687d917 cached
147 files
914.1 KB
274.2k tokens
1012 symbols
1 requests
Download .txt
Showing preview only (959K chars total). Download the full file or copy to clipboard to get everything.
Repository: JoeanAmier/TikTokDownloader
Branch: master
Commit: d9c12687d917
Files: 147
Total size: 914.1 KB

Directory structure:
gitextract_3f4xxgla/

├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── dependabot.yml
│   └── workflows/
│       ├── Close_Stale_Issues_and_PRs.yaml
│       ├── Delete_untagged_images.yml
│       ├── Manually_build_executable_programs.yml
│       ├── Manually_docker_image.yml
│       ├── Release_build_executable_program.yml
│       └── Release_docker_image.yml
├── .gitignore
├── .python-version
├── Dockerfile
├── README.md
├── README_EN.md
├── docs/
│   ├── Cookie获取教程.md
│   ├── DouK-Downloader文档.md
│   └── Release_Notes.md
├── license
├── locale/
│   ├── README.md
│   ├── en_US/
│   │   └── LC_MESSAGES/
│   │       ├── tk.mo
│   │       └── tk.po
│   ├── generate_path.py
│   ├── po_to_mo.py
│   ├── tk.pot
│   └── zh_CN/
│       └── LC_MESSAGES/
│           ├── tk.mo
│           └── tk.po
├── main.py
├── pyproject.toml
├── requirements.txt
├── src/
│   ├── application/
│   │   ├── TikTokDownloader.py
│   │   ├── __init__.py
│   │   ├── main_monitor.py
│   │   ├── main_server.py
│   │   └── main_terminal.py
│   ├── cli_edition/
│   │   ├── __init__.py
│   │   ├── main_cli.py
│   │   └── write.py
│   ├── config/
│   │   ├── __init__.py
│   │   ├── parameter.py
│   │   └── settings.py
│   ├── custom/
│   │   ├── __init__.py
│   │   ├── function.py
│   │   ├── internal.py
│   │   └── static.py
│   ├── downloader/
│   │   ├── __init__.py
│   │   └── download.py
│   ├── encrypt/
│   │   ├── __init__.py
│   │   ├── aBogus.py
│   │   ├── device_id.py
│   │   ├── msToken.py
│   │   ├── ttWid.py
│   │   ├── verifyFp.py
│   │   ├── webID.py
│   │   ├── xBogus.py
│   │   └── xGnarly.py
│   ├── extract/
│   │   ├── __init__.py
│   │   └── extractor.py
│   ├── gui_edition/
│   │   └── __init__.py
│   ├── interface/
│   │   ├── __init__.py
│   │   ├── account.py
│   │   ├── account_tiktok.py
│   │   ├── collection.py
│   │   ├── collects.py
│   │   ├── comment.py
│   │   ├── comment_tiktok.py
│   │   ├── detail.py
│   │   ├── detail_tiktok.py
│   │   ├── hashtag.py
│   │   ├── hot.py
│   │   ├── info.py
│   │   ├── info_tiktok.py
│   │   ├── live.py
│   │   ├── live_tiktok.py
│   │   ├── mix.py
│   │   ├── mix_tiktok.py
│   │   ├── search.py
│   │   ├── slides.py
│   │   ├── template.py
│   │   └── user.py
│   ├── link/
│   │   ├── __init__.py
│   │   ├── extractor.py
│   │   └── requester.py
│   ├── manager/
│   │   ├── __init__.py
│   │   ├── cache.py
│   │   ├── database.py
│   │   └── recorder.py
│   ├── models/
│   │   ├── __init__.py
│   │   ├── account.py
│   │   ├── base.py
│   │   ├── comment.py
│   │   ├── detail.py
│   │   ├── live.py
│   │   ├── mix.py
│   │   ├── reply.py
│   │   ├── response.py
│   │   ├── search.py
│   │   ├── settings.py
│   │   └── share.py
│   ├── module/
│   │   ├── __init__.py
│   │   ├── cookie.py
│   │   ├── ffmpeg.py
│   │   ├── migrate_folder.py
│   │   ├── register.py
│   │   ├── tiktok_account_index.py
│   │   └── tiktok_unofficial.py
│   ├── record/
│   │   ├── __init__.py
│   │   ├── base.py
│   │   └── logger.py
│   ├── storage/
│   │   ├── __init__.py
│   │   ├── csv.py
│   │   ├── manager.py
│   │   ├── mysql.py
│   │   ├── sql.py
│   │   ├── sqlite.py
│   │   ├── text.py
│   │   └── xlsx.py
│   ├── testers/
│   │   ├── __init__.py
│   │   ├── logger.py
│   │   ├── params.py
│   │   ├── test_format.py
│   │   └── translate.py
│   ├── tools/
│   │   ├── __init__.py
│   │   ├── browser.py
│   │   ├── capture.py
│   │   ├── choose.py
│   │   ├── cleaner.py
│   │   ├── console.py
│   │   ├── error.py
│   │   ├── file_folder.py
│   │   ├── format.py
│   │   ├── list_pop.py
│   │   ├── progress.py
│   │   ├── rename_compatible.py
│   │   ├── retry.py
│   │   ├── session.py
│   │   ├── temporary.py
│   │   ├── timer.py
│   │   └── truncate.py
│   ├── translation/
│   │   ├── __init__.py
│   │   ├── static.py
│   │   └── translate.py
│   └── tui_edition/
│       ├── __init__.py
│       ├── app.py
│       └── setting.py
└── static/
    ├── images/
    │   └── DouK-Downloader.icns
    └── js/
        ├── X-Bogus.js
        └── a_bogus.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: 报告项目问题
title: '[功能异常] '
labels: ''
assignees: JoeanAmier

---

**问题描述**

清晰简洁地描述该错误是什么。

A clear and concise description of what the bug is.

**重现步骤**

重现该问题的步骤:

Steps to reproduce the behavior:

1. ...
2. ...
3. ...

**预期结果**

清晰简洁地描述您预期会发生的情况。

A clear and concise description of what you expected to happen.

**补充信息**

在此添加有关该问题的任何其他上下文信息,例如:操作系统、运行方式、配置文件、错误截图、运行日志等。

请注意:提供配置文件时,请删除 Cookie 内容,避免敏感数据泄露!

Add any other contextual information about the issue here, such as operating system, runtime mode, configuration files,
error screenshots, runtime logs, etc.

Please note: When providing configuration files, please delete cookie content to avoid sensitive data leakage!


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: 功能优化建议
title: '[优化建议] '
labels: ''
assignees: JoeanAmier

---

**功能请求**

清晰简洁地描述问题是什么。例如:当 [...] 时,我总是感到沮丧。

A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**描述您希望的解决方案**

清晰简洁地描述您希望发生的情况。

A clear and concise description of what you want to happen.

**描述您考虑过的替代方案**

清晰简洁地描述您考虑过的任何替代解决方案或功能。

A clear and concise description of any alternative solutions or features you've considered.

**补充信息**

在此添加有关功能请求的任何其他上下文或截图。

Add any other context or screenshots about the feature request here.


================================================
FILE: .github/dependabot.yml
================================================
version: 2
registries:
  pip_mirror:
    type: python-index
    url: https://mirrors.ustc.edu.cn/pypi/simple
updates:
  - package-ecosystem: "uv"
    directory: "/"
    schedule:
      interval: "cron"
      cronjob: "0 0 * * 6"
      timezone: "Asia/Shanghai"
    target-branch: "develop"
    registries:
      - pip_mirror


================================================
FILE: .github/workflows/Close_Stale_Issues_and_PRs.yaml
================================================
name: "自动管理过时的问题和PR"
on:
  schedule:
    - cron: "0 0 * * 6"
  workflow_dispatch:

permissions:
  issues: write
  pull-requests: write

jobs:
  stale:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/stale@v9
        with:
          stale-issue-message: |
            ⚠️ 此 Issue 已超过一定时间未活动,如果没有进一步更新,将在 14 天后关闭。
            ⚠️ This issue has been inactive for a certain period of time. If there are no further updates, it will be closed in 14 days.
          close-issue-message: |
            🔒 由于长时间未响应,此 Issue 已被自动关闭。如有需要,请重新打开或提交新 issue。
            🔒 Due to prolonged inactivity, this issue has been automatically closed. If needed, please reopen it or submit a new issue.
          stale-pr-message: |
            ⚠️ 此 PR 已超过一定时间未更新,请更新,否则将在 14 天后关闭。
            ⚠️ This PR has not been updated for a certain period of time. Please update it, otherwise it will be closed in 14 days.
          close-pr-message: |
            🔒 此 PR 已因无更新而自动关闭。如仍需合并,请重新打开或提交新 PR。
            🔒 This PR has been automatically closed due to inactivity. If you still wish to merge it, please reopen it or submit a new PR.

          days-before-stale: 28
          days-before-close: 14

          ascending: true

          stale-issue-label: "未跟进问题(Stale)"
          close-issue-label: "自动关闭(Close)"
          stale-pr-label: "未跟进问题(Stale)"
          close-pr-label: "自动关闭(Close)"
          exempt-issue-labels: "功能异常(bug),文档补充(docs),功能优化(enhancement),适合新手(good first issue),"
          exempt-pr-labels: "功能异常(bug),文档补充(docs),功能优化(enhancement),适合新手(good first issue),"


================================================
FILE: .github/workflows/Delete_untagged_images.yml
================================================
name: 删除 GHCR Untagged 镜像
on:
  schedule:
    - cron: "0 0 15 * *"
  release:
    types: [ published ]
  workflow_dispatch:

jobs:
  delete-untagged:
    runs-on: ubuntu-latest
    steps:
      - name: Delete all containers from package without tags
        uses: Chizkiyahu/delete-untagged-ghcr-action@v6
        with:
          token: ${{ secrets.PAT_TOKEN }}
          repository_owner: ${{ github.repository_owner }}
          repository: ${{ github.repository }}
          package_name: "tiktok-downloader"
          untagged_only: true
          owner_type: user


================================================
FILE: .github/workflows/Manually_build_executable_programs.yml
================================================
name: 构建可执行文件

on:
  workflow_dispatch:

jobs:
  build:
    name: 构建于 ${{ matrix.os }}
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ windows-latest, windows-11-arm, macos-15-intel, macos-latest ]

    steps:
      - name: 签出存储库
        uses: actions/checkout@v4

      - name: 设置 Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: 安装依赖项
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
          pip install pyinstaller

      - name: 构建 Win 可执行文件
        if: runner.os == 'Windows'
        run: |
          echo "DATE=$(Get-Date -Format 'yyyyMMdd')" >> $env:GITHUB_ENV
          pyinstaller --icon=./static/images/DouK-Downloader.ico --add-data "static:static" --add-data "locale:locale" --collect-all rich main.py
        shell: pwsh

      - name: 构建 Mac 可执行文件
        if: runner.os == 'macOS'
        run: |
          echo "DATE=$(date +'%Y%m%d')" >> $GITHUB_ENV
          pyinstaller --icon=./static/images/DouK-Downloader.icns --add-data "static:static" --add-data "locale:locale" --collect-all rich main.py

      - name: 上传文件
        uses: actions/upload-artifact@v4
        with:
          name: DouK-Downloader_${{ runner.os }}_${{ runner.arch }}_${{ env.DATE }}
          path: dist/main/


================================================
FILE: .github/workflows/Manually_docker_image.yml
================================================
name: 构建并发布 Docker 镜像

on:
  workflow_dispatch:
    inputs:
      is_beta:
        type: boolean
        required: true
        description: "开发版"
        default: true
      custom_version:
        type: string
        required: false
        description: "版本号"
        default: ""

permissions:
  contents: read
  packages: write
  attestations: write
  id-token: write

env:
  REGISTRY: ghcr.io
  DOCKER_REPO: ${{ secrets.DOCKERHUB_USERNAME }}/tiktok-downloader
  GHCR_REPO: ghcr.io/${{ secrets.DOCKERHUB_USERNAME }}/tiktok-downloader

jobs:
  publish-docker:
    runs-on: ubuntu-latest

    steps:
      - name: 拉取源码
        uses: actions/checkout@v4
        with:
          fetch-depth: 1

      - name: 获取最新的发布标签
        id: get-latest-release
        run: |
          if [ -z "${{ github.event.inputs.custom_version }}" ]; then
            LATEST_TAG=$(curl -s \
              -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
              https://api.github.com/repos/${{ github.repository }}/releases/latest \
              | jq -r '.tag_name')
          else
            LATEST_TAG=${{ github.event.inputs.custom_version }}
          fi
          if [ -z "$LATEST_TAG" ]; then
            exit 1
          fi
          echo "LATEST_TAG=$LATEST_TAG" >> $GITHUB_ENV

      - name: 设置 QEMU
        uses: docker/setup-qemu-action@v3

      - name: 设置 Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: 生成标签
        id: generate-tags
        run: |
          if [ "${{ inputs.is_beta }}" == "true" ]; then
            LATEST_TAG="${LATEST_TAG%.*}.$(( ${LATEST_TAG##*.} + 1 ))"
            echo "LATEST_TAG=$LATEST_TAG" >> $GITHUB_ENV
            TAGS="${{ env.DOCKER_REPO }}:${LATEST_TAG}-dev,${{ env.GHCR_REPO }}:${LATEST_TAG}-dev"
          else
            TAGS="${{ env.DOCKER_REPO }}:${LATEST_TAG},${{ env.DOCKER_REPO }}:latest,${{ env.GHCR_REPO }}:${LATEST_TAG},${{ env.GHCR_REPO }}:latest"
          fi
          echo "TAGS=$TAGS" >> $GITHUB_ENV

      - name: 登录到 Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: 登录到 GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: 构建和推送 Docker 镜像到 Docker Hub 和 GHCR
        uses: docker/build-push-action@v6
        with:
          context: .
          platforms: linux/amd64,linux/arm64,linux/arm/v7
          push: true
          tags: ${{ env.TAGS }}
          provenance: false
          sbom: false


================================================
FILE: .github/workflows/Release_build_executable_program.yml
================================================
name: 自动构建并发布可执行文件

on:
  release:
    types: [ published ]

permissions:
  contents: write
  discussions: write

jobs:
  build:
    name: 构建于 ${{ matrix.os }}
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ windows-latest, windows-11-arm, macos-15-intel, macos-latest ]

    steps:
      - name: 签出存储库
        uses: actions/checkout@v4

      - name: 设置 Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: 安装依赖项
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
          pip install pyinstaller

      - name: 构建 Win 可执行文件
        if: runner.os == 'Windows'
        run: |
          pyinstaller --icon=./static/images/DouK-Downloader.ico --add-data "static:static" --add-data "locale:locale" --collect-all rich main.py
        shell: pwsh

      - name: 构建 Mac 可执行文件
        if: runner.os == 'macOS'
        run: |
          pyinstaller --icon=./static/images/DouK-Downloader.icns --add-data "static:static" --add-data "locale:locale" --collect-all rich main.py

      - name: 创建压缩包
        run: |
          7z a "DouK-Downloader_V${{ github.event.release.tag_name }}_${{ runner.os }}_${{ runner.arch }}.zip" ./dist/main/*
        shell: bash

      - name: 上传文件到 release
        uses: softprops/action-gh-release@v2
        with:
          files: |
            ./DouK-Downloader_V*.zip
          name: DouK-Downloader V${{ github.event.release.tag_name }}
          body_path: ./docs/Release_Notes.md
          draft: ${{ github.event.release.draft }}
          prerelease: ${{ github.event.release.prerelease }}


================================================
FILE: .github/workflows/Release_docker_image.yml
================================================
name: 自动构建并发布 Docker 镜像

on:
  release:
    types: [ published ]

permissions:
  contents: read
  packages: write
  attestations: write
  id-token: write

env:
  REGISTRY: ghcr.io
  DOCKER_REPO: ${{ secrets.DOCKERHUB_USERNAME }}/tiktok-downloader
  GHCR_REPO: ghcr.io/${{ secrets.DOCKERHUB_USERNAME }}/tiktok-downloader

jobs:
  publish-docker:
    runs-on: ubuntu-latest

    steps:
      - name: 拉取源码
        uses: actions/checkout@v4
        with:
          fetch-depth: 1

      - name: 设置 QEMU
        uses: docker/setup-qemu-action@v3

      - name: 设置 Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: 登录到 Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: 登录到 GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: 构建和推送 Docker 镜像到 Docker Hub 和 GHCR
        uses: docker/build-push-action@v6
        with:
          context: .
          platforms: linux/amd64,linux/arm64,linux/arm/v7
          push: true
          tags: |
            ${{ env.DOCKER_REPO }}:${{ github.event.release.tag_name }}
            ${{ env.DOCKER_REPO }}:latest
            ${{ env.GHCR_REPO }}:${{ github.event.release.tag_name }}
            ${{ env.GHCR_REPO }}:latest
          provenance: false
          sbom: false


================================================
FILE: .gitignore
================================================
__pycache__/
*.pyc
/.venv/
/.ruff_cache/
/.idea/
/.run/
/Volume/
!/.github/


================================================
FILE: .python-version
================================================
3.12


================================================
FILE: Dockerfile
================================================
# ---- 阶段 1: 构建器 (Builder) ----
# 使用一个功能完整的镜像,它包含编译工具或可以轻松安装它们
FROM python:3.12-bullseye as builder

# 安装编译 uvloop 和 httptools 所需的系统依赖 (C编译器等)
RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential \
    && rm -rf /var/lib/apt/lists/*

# 设置工作目录
WORKDIR /app

# 复制需求文件
COPY requirements.txt .

# 在这个具备编译环境的阶段安装所有 Python 依赖
# 安装到一个独立的目录 /install 中,以便后续复制
RUN pip install --no-cache-dir --prefix="/install" -r requirements.txt

# ---- 阶段 2: 最终镜像 (Final Image) ----
# 使用轻量级 slim 镜像作为最终的运行环境
FROM python:3.12-slim

# 设置工作目录
WORKDIR /app

# 添加元数据标签
LABEL name="DouK-Downloader" authors="JoeanAmier" repository="https://github.com/JoeanAmier/TikTokDownloader"

# 从构建器阶段,将已经安装好的依赖包复制到最终镜像的系统路径中
COPY --from=builder /install /usr/local

# 复制你的应用程序代码和相关文件
COPY src /app/src
COPY locale /app/locale
COPY static /app/static
COPY license /app/license
COPY main.py /app/main.py

# 暴露端口
EXPOSE 5555

# 创建挂载点
VOLUME /app/Volume

# 设置容器启动命令
CMD ["python", "main.py"]


================================================
FILE: README.md
================================================
<div align="center">
<img src="./static/images/DouK-Downloader.png" alt="DouK-Downloader" height="256" width="256"><br>
<h1>DouK-Downloader</h1>
<p>简体中文 | <a href="README_EN.md">English</a></p>
<a href="https://trendshift.io/repositories/6222" target="_blank"><img src="https://trendshift.io/api/badge/repositories/6222" alt="" style="width: 250px; height: 55px;" width="250" height="55"/></a>
<br>
<img alt="GitHub" src="https://img.shields.io/github/license/JoeanAmier/TikTokDownloader?style=flat-square">
<img alt="GitHub forks" src="https://img.shields.io/github/forks/JoeanAmier/TikTokDownloader?style=flat-square&color=55efc4">
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/JoeanAmier/TikTokDownloader?style=flat-square&color=fda7df">
<img alt="GitHub code size in bytes" src="https://img.shields.io/github/languages/code-size/JoeanAmier/TikTokDownloader?style=flat-square&color=a29bfe">
<br>
<img alt="Static Badge" src="https://img.shields.io/badge/Python-3.12-b8e994?style=flat-square&logo=python&labelColor=3dc1d3">
<img alt="GitHub release (with filter)" src="https://img.shields.io/github/v/release/JoeanAmier/TikTokDownloader?style=flat-square&color=48dbfb">
<img src="https://img.shields.io/badge/Sourcery-enabled-884898?style=flat-square&color=1890ff" alt="">
<img alt="Static Badge" src="https://img.shields.io/badge/Docker-badc58?style=flat-square&logo=docker">
<img alt="GitHub all releases" src="https://img.shields.io/github/downloads/JoeanAmier/TikTokDownloader/total?style=flat-square&color=ffdd59">
</div>
<br>
<p>🔥 <b>TikTok 发布/喜欢/合辑/直播/视频/图集/音乐;抖音发布/喜欢/收藏/收藏夹/视频/图集/实况/直播/音乐/合集/评论/账号/搜索/热榜数据采集工具:</b>完全开源,基于 HTTPX 模块实现的免费数据采集和文件下载工具;批量下载抖音账号发布、喜欢、收藏、收藏夹作品;批量下载 TikTok 账号发布、喜欢作品;下载抖音链接或 TikTok 链接作品;获取抖音直播拉流地址;下载抖音直播视频;获取 TikTok 直播拉流地址;下载 TikTok 直播视频;采集抖音作品评论数据;批量下载抖音合集作品;批量下载 TikTok 合辑作品;采集抖音账号详细数据;采集抖音用户 / 作品 / 直播搜索结果;采集抖音热榜数据。</p>
<p>⭐ 本项目历史名称:<code>TikTokDownloader</code></p>
<p>📣 本项目将于未来进行代码结构重构,目标是让代码更加稳健,并具备更好的可维护性与扩展性;如果你对项目设计、实现方式或优化思路有想法,欢迎提出建议或参与讨论!</p>
<hr>

# 📝 项目功能

<details>
<summary>功能列表(点击展开)</summary>
<ul>
<li>✅ 下载抖音视频/图集</li>
<li>✅ 下载抖音实况/动图</li>
<li>✅ 下载最高画质视频文件</li>
<li>✅ 下载 TikTok 视频原画</li>
<li>✅ 下载 TikTok 视频/图集</li>
<li>✅ 下载抖音账号发布/喜欢/收藏/收藏夹作品</li>
<li>✅ 下载 TikTok 账号发布/喜欢作品</li>
<li>✅ 采集抖音 / TikTok 详细数据</li>
<li>✅ 批量下载链接作品</li>
<li>✅ 多账号批量下载作品</li>
<li>✅ 自动跳过已下载的文件</li>
<li>✅ 持久化保存采集数据</li>
<li>✅ 支持 CSV/XLSX/SQLite 格式保存数据</li>
<li>✅ 下载动态/静态封面图</li>
<li>✅ 获取抖音直播拉流地址</li>
<li>✅ 获取 TikTok 直播拉流地址</li>
<li>✅ 调用 ffmpeg 下载直播</li>
<li>✅ Web UI 交互界面</li>
<li>✅ 采集抖音作品评论数据</li>
<li>✅ 下载抖音合集作品</li>
<li>✅ 下载 TikTok 合辑作品</li>
<li>✅ 记录点赞收藏等统计数据</li>
<li>✅ 筛选作品发布时间</li>
<li>✅ 支持账号作品增量下载</li>
<li>✅ 支持使用代理采集数据</li>
<li>✅ 支持局域网远程访问</li>
<li>✅ 采集抖音账号详细数据</li>
<li>✅ 作品统计数据更新</li>
<li>✅ 支持自定义账号/合集标识</li>
<li>✅ 自动更新账号昵称/标识</li>
<li>✅ 部署至私有服务器</li>
<li>✅ 部署至公开服务器</li>
<li>✅ 采集抖音搜索数据</li>
<li>✅ 采集抖音热榜数据</li>
<li>✅ 记录已下载作品 ID</li>
<li>☑️ <del>扫码登陆获取 Cookie</del></li>
<li>✅ 从浏览器读取 Cookie</li>
<li>✅ 支持 Web API 调用</li>
<li>✅ 支持多线程下载作品</li>
<li>✅ 文件完整性处理机制</li>
<li>✅ 自定义规则筛选作品</li>
<li>✅ 按文件夹归档保存作品文件</li>
<li>✅ 自定义设置文件大小上限</li>
<li>✅ 支持文件断点续传下载</li>
<li>✅ 监听剪贴板链接下载作品</li>
</ul>
</details>

# 💻 程序截图

<p><a href="https://www.bilibili.com/video/BV1d7eAzTEFs/">前往 bilibili 观看演示</a>;<a href="https://youtu.be/yMU-RWl55hg">前往 YouTube 观看演示</a></p>

## 终端交互模式

<p>建议通过配置文件管理账号,更多介绍请查阅 <a href="https://github.com/JoeanAmier/TikTokDownloader/wiki/Documentation">文档</a></p>

![终端模式截图](docs/screenshot/终端交互模式截图CN1.png)
*****
![终端模式截图](docs/screenshot/终端交互模式截图CN2.png)
*****
![终端模式截图](docs/screenshot/终端交互模式截图CN3.png)

## Web UI 交互模式

> **项目代码已重构,该模式代码尚未更新,未来开发完成重新开放!**

## Web API 接口模式

![WebAPI模式截图](docs/screenshot/WebAPI模式截图CN1.png)
*****
![WebAPI模式截图](docs/screenshot/WebAPI模式截图CN2.png)

> **启动该模式后,访问 `http://127.0.0.1:5555/docs` 或者 `http://127.0.0.1:5555/redoc` 可以查阅自动生成的文档!**

### API 调用示例代码

```python
from httpx import post
from rich import print


def demo():
    headers = {"token": ""}
    data = {
        "detail_id": "0123456789",
        "pages": 2,
    }
    api = "http://127.0.0.1:5555/douyin/comment"
    response = post(api, json=data, headers=headers)
    print(response.json())


demo()
```

# 📋 项目说明

## 快速入门

<p>⭐ Mac OS、Windows 10 及以上用户可前往 <a href="https://github.com/JoeanAmier/TikTokDownloader/releases/latest">Releases</a> 或者 <a href="https://github.com/JoeanAmier/TikTokDownloader/actions">Actions</a> 下载已编译的程序,开箱即用!</p>
<p>⭐ 本项目包含自动构建可执行文件的 GitHub Actions,使用者可以随时使用 GitHub Actions 将最新源码构建为可执行文件!</p>
<p>⭐ 自动构建可执行文件教程请查阅本文档的 <code>构建可执行文件指南</code> 部分;如果需要更加详细的图文教程,请 <a href="https://mp.weixin.qq.com/s/TorfoZKkf4-x8IBNLImNuw">查阅文章</a>!</p>
<p><strong>注意:由于 Mac OS 平台的可执行文件 <code>main</code> 未经过代码签名,首次运行时会受到系统安全限制。请先在终端执行 <code>xattr -cr 项目文件夹路径</code> 命令移除安全标记,执行一次后即可正常运行。</strong></p>
<hr>
<ol>
<li><b>运行可执行文件</b> 或者 <b>配置环境运行</b>(二选一)
<ol><b>运行可执行文件</b>
<li>下载 <a href="https://github.com/JoeanAmier/TikTokDownloader/releases/latest">Releases</a> 或者 Actions 构建的可执行文件压缩包</li>
<li>解压后打开程序文件夹,双击运行 <code>main</code></li>
</ol>
<ol><b>配置环境运行</b>

[//]: # (<li>安装不低于 <code>3.12</code> 版本的 <a href="https://www.python.org/">Python</a> 解释器</li>)
<li>安装 <code>3.12</code> 版本的 <a href="https://www.python.org/">Python</a> 解释器</li>
<li>下载最新的源码或 <a href="https://github.com/JoeanAmier/TikTokDownloader/releases/latest">Releases</a> 发布的源码至本地</li>
<ol><b>使用 pip 安装项目依赖</b>
<li>运行 <code>python -m venv venv</code> 命令创建虚拟环境(可选)</li>
<li>运行 <code>.\venv\Scripts\activate.ps1</code> 或者 <code>venv\Scripts\activate</code> 命令激活虚拟环境(可选)</li>
<li>运行 <code>pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt</code> 命令安装程序所需模块</li>
<li>运行 <code>python .\main.py</code> 或者 <code>python main.py</code> 命令启动 DouK-Downloader</li>
</ol>
<ol><b>使用 uv 安装项目依赖(推荐)</b>
<li>运行 <code>uv sync --no-dev</code> 命令同步环境依赖</li>
<li>运行 <code>uv run main.py</code> 命令启动 DouK-Downloader</li>
</ol>
</ol>
</li>
<li>阅读 DouK-Downloader 的免责声明,根据提示输入内容</li>
<li>将 Cookie 信息写入配置文件
<ol><b>从剪贴板读取 Cookie(推荐)</b>
<li>参考 <a href="https://github.com/JoeanAmier/TikTokDownloader/blob/master/docs/Cookie%E8%8E%B7%E5%8F%96%E6%95%99%E7%A8%8B.md">Cookie 提取教程</a>,复制所需 Cookie 至剪贴板</li>
<li>选择 <code>从剪贴板读取 Cookie</code> 选项,程序会自动读取剪贴板的 Cookie 并写入配置文件</li>
</ol>
<ol><b>从浏览器读取 Cookie</b>
<li>选择 <code>从浏览器读取 Cookie</code> 选项,按照提示输入浏览器类型或序号</li>
</ol>
<ol><b><del>扫码登录获取 Cookie</del>(失效)</b>
<li><del>选择 <code>扫码登录获取 Cookie</code> 选项,程序会显示登录二维码图片,并使用默认应用打开图片</del></li>
<li><del>使用抖音 APP 扫描二维码并登录账号</del></li>
<li><del>按照提示操作,程序会自动将 Cookie 写入配置文件</del></li>
</ol>
</li>
<li>返回程序界面,依次选择 <code>终端交互模式</code> -> <code>批量下载链接作品(通用)</code> -> <code>手动输入待采集的作品链接</code></li>
<li>输入抖音作品链接即可下载作品文件(TikTok 平台需要更多初始设置,详见文档)</li>
<li>更多详细说明请查看 <b><a href="https://github.com/JoeanAmier/TikTokDownloader/wiki/Documentation">项目文档</a></b></li>
</ol>
<p>⭐ 推荐使用 <a href="https://learn.microsoft.com/zh-cn/windows/terminal/install">Windows 终端</a>(Windows 11 自带默认终端)</p>

### Docker 容器

<ol>
<li>获取镜像</li>
<ul>
<li>方式一:使用 <code>Dockerfile</code> 文件构建镜像</li>
<li>方式二:使用 <code>docker pull joeanamier/tiktok-downloader</code> 命令拉取镜像</li>
<li>方式三:使用 <code>docker pull ghcr.io/joeanamier/tiktok-downloader</code> 命令拉取镜像</li>
</ul>
<li>创建容器:<code>docker run --name 容器名称(可选) -p 主机端口号:5555 -v tiktok_downloader_volume:/app/Volume -it &lt;镜像名称&gt;</code>
</li>
<br><b>注意:</b>此处的 <code>&lt;镜像名称&gt;</code> 需与您在第一步中使用的镜像名称保持一致(例如 <code>joeanamier/tiktok-downloader</code> 或 <code>ghcr.io/joeanamier/tiktok-downloader</code>)
<li>运行容器
<ul>
<li>启动容器:<code>docker start -i 容器名称/容器 ID</code></li>
<li>重启容器:<code>docker restart -i 容器名称/容器 ID</code></li>
</ul>
</li>
</ol>
<p>Docker 容器无法直接访问宿主机的文件系统,部分功能不可用,例如:<code>从浏览器读取 Cookie</code>;其他功能如有异常请反馈!</p>
<hr>

## 关于 Cookie

[点击查看 Cookie 获取教程](https://github.com/JoeanAmier/TikTokDownloader/blob/master/docs/Cookie%E8%8E%B7%E5%8F%96%E6%95%99%E7%A8%8B.md)

> * Cookie 仅需在失效后重新写入配置文件,并非每次运行程序都要写入配置文件!
>
> * Cookie 会影响下载的视频文件分辨率,如果无法下载最高分辨率的视频文件,请尝试更新 Cookie!
>
> * 程序获取数据失败时,可以尝试更新 Cookie 或者使用已登录的 Cookie!

<hr>

## 其他说明

<ul>
<li>程序提示用户输入时,直接回车代表返回上级菜单,输入 <code>Q</code> 或 <code>q</code> 代表结束运行</li>
<li>由于获取账号喜欢作品和收藏作品数据仅返回喜欢 / 收藏作品的发布日期,不返回操作日期,因此程序需要获取全部喜欢 / 收藏作品数据再进行日期筛选;如果作品数量较多,可能会花费较长的时间;可通过 <code>max_pages</code> 参数控制请求次数</li>
<li>获取私密账号的发布作品数据需要登录后的 Cookie,且登录的账号需要关注该私密账号</li>
<li>批量下载账号作品或合集作品时,如果对应的昵称或标识发生变化,程序会自动更新已下载作品文件名称中的昵称和标识</li>
<li>程序下载文件时会先将文件下载至临时文件夹,下载完成后再移动至储存文件夹;程序运行结束时会清空临时文件夹</li>
<li><code>批量下载收藏作品模式</code> 目前仅支持下载当前已登录 Cookie 对应账号的收藏作品,暂不支持多账号</li>
<li>如果想要程序使用代理请求数据,必须在 <code>settings.json</code> 设置 <code>proxy</code> 参数,否则程序不会使用代理</li>
<li>如果您的计算机没有合适的程序编辑 JSON 文件,建议使用 <a href="https://www.toolhelper.cn/JSON/JSONFormat">在线工具</a> 编辑配置文件内容,修改后需要重启软件才能生效。</li>
<li>当程序请求用户输入内容或链接时,请注意避免输入的内容或链接包含换行符,这可能会导致预期之外的问题</li>
<li>本项目不会支持付费作品下载,请勿反馈任何关于付费作品下载的问题</li>
<li>Windows 系统需要以管理员身份运行程序才能读取 Chromium、Chrome、Edge 浏览器 Cookie</li>
<li>本项目并未针对程序多开的情况进行优化,如需程序多开,请复制整个项目的文件夹,避免出现预期之外的问题</li>
<li>程序运行过程中,如需终止程序或 <code>ffmpeg</code>,请按下 <code>Ctrl + C</code> 终止运行,不要直接点击终端窗口的关闭按钮</li>
</ul>
<h2>构建可执行文件指南</h2>
<details>
<summary><b>构建可执行文件指南(点击展开)</b></summary>

本指南将引导您通过 Fork 本仓库并执行 GitHub Actions 自动完成基于最新源码的程序构建和打包!

---

### 使用步骤

#### 1. Fork 本仓库

1. 点击项目仓库右上角的 **Fork** 按钮,将本仓库 Fork 到您的个人 GitHub 账户中
2. 您的 Fork 仓库地址将类似于:`https://github.com/your-username/this-repo`

---

#### 2. 启用 GitHub Actions

1. 前往您 Fork 的仓库页面
2. 点击顶部的 **Settings** 选项卡
3. 点击右侧的 **Actions** 选项卡
4. 点击 **General** 选项
5. 在 **Actions permissions** 下,选择 **Allow all actions and reusable workflows** 选项,点击 **Save** 按钮

---

#### 3. 手动触发打包流程

1. 在您 Fork 的仓库中,点击顶部的 **Actions** 选项卡
2. 找到名为 **构建可执行文件** 的工作流
3. 点击右侧的 **Run workflow** 按钮:
    - 选择 **master** 或者 **develop** 分支
    - 点击 **Run workflow**

---

#### 4. 查看打包进度

1. 在 **Actions** 页面中,您可以看到触发的工作流运行记录
2. 点击运行记录,查看详细的日志以了解打包进度和状态

---

#### 5. 下载打包结果

1. 打包完成后,进入对应的运行记录页面
2. 在页面底部的 **Artifacts** 部分,您将看到打包的结果文件
3. 点击下载并保存到本地,即可获得打包好的程序

---

### 注意事项

1. **资源使用**:
    - Actions 的运行环境由 GitHub 免费提供,普通用户每月有一定的免费使用额度(2000 分钟)

2. **代码修改**:
    - 您可以自由修改 Fork 仓库中的代码以定制程序打包流程
    - 修改后重新触发打包流程,您将得到自定义的构建版本

3. **与主仓库保持同步**:
    - 如果主仓库更新了代码或工作流,建议您定期同步 Fork 仓库以获取最新功能和修复

---

### Actions 常见问题

#### Q1: 为什么我无法触发工作流?

A: 请确认您已按照步骤 **启用 Actions**,否则 GitHub 会禁止运行工作流

#### Q2: 打包流程失败怎么办?

A:

- 检查运行日志,了解失败原因
- 确保代码没有语法错误或依赖问题
- 如果问题仍未解决,可以在本仓库的 [Issues 页面](https://github.com/JoeanAmier/TikTokDownloader/issues) 提出问题

#### Q3: 我可以直接使用主仓库的 Actions 吗?

A: 由于权限限制,您无法直接触发主仓库的 Actions。请通过 Fork 仓库的方式执行打包流程

</details>

## 程序更新

<p><strong>方案一:</strong>下载并解压文件,将旧版本的 <code>_internal\Volume</code> 文件夹复制到新版本的 <code>_internal</code> 文件夹。</p>
<p><strong>方案二:</strong>下载并解压文件(不要运行程序),复制全部文件,直接覆盖旧版本文件。</p>

# ⚠️ 免责声明

<ol>
<li>使用者对本项目的使用由使用者自行决定,并自行承担风险。作者对使用者使用本项目所产生的任何损失、责任、或风险概不负责。</li>
<li>本项目的作者提供的代码和功能是基于现有知识和技术的开发成果。作者按现有技术水平努力确保代码的正确性和安全性,但不保证代码完全没有错误或缺陷。</li>
<li>本项目依赖的所有第三方库、插件或服务各自遵循其原始开源或商业许可,使用者需自行查阅并遵守相应协议,作者不对第三方组件的稳定性、安全性及合规性承担任何责任。</li>
<li>使用者在使用本项目时必须严格遵守 <a href="https://github.com/JoeanAmier/TikTokDownloader/blob/master/LICENSE">GNU
    General Public License v3.0</a> 的要求,并在适当的地方注明使用了 <a
        href="https://github.com/JoeanAmier/TikTokDownloader/blob/master/LICENSE">GNU General Public License
    v3.0</a> 的代码。
</li>
<li>使用者在使用本项目的代码和功能时,必须自行研究相关法律法规,并确保其使用行为合法合规。任何因违反法律法规而导致的法律责任和风险,均由使用者自行承担。</li>
<li>使用者不得使用本工具从事任何侵犯知识产权的行为,包括但不限于未经授权下载、传播受版权保护的内容,开发者不参与、不支持、不认可任何非法内容的获取或分发。</li>
<li>本项目不对使用者涉及的数据收集、存储、传输等处理活动的合规性承担责任。使用者应自行遵守相关法律法规,确保处理行为合法正当;因违规操作导致的法律责任由使用者自行承担。</li>
<li>使用者在任何情况下均不得将本项目的作者、贡献者或其他相关方与使用者的使用行为联系起来,或要求其对使用者使用本项目所产生的任何损失或损害负责。</li>
<li>本项目的作者不会提供 DouK-Downloader 项目的付费版本,也不会提供与 DouK-Downloader 项目相关的任何商业服务。</li>
<li>基于本项目进行的任何二次开发、修改或编译的程序与原创作者无关,原创作者不承担与二次开发行为或其结果相关的任何责任,使用者应自行对因二次开发可能带来的各种情况负全部责任。</li>
<li>本项目不授予使用者任何专利许可;若使用本项目导致专利纠纷或侵权,使用者自行承担全部风险和责任。未经作者或权利人书面授权,不得使用本项目进行任何商业宣传、推广或再授权。</li>
<li>作者保留随时终止向任何违反本声明的使用者提供服务的权利,并可能要求其销毁已获取的代码及衍生作品。</li>
<li>作者保留在不另行通知的情况下更新本声明的权利,使用者持续使用即视为接受修订后的条款。</li>
</ol>
<b>在使用本项目的代码和功能之前,请您认真考虑并接受以上免责声明。如果您对上述声明有任何疑问或不同意,请不要使用本项目的代码和功能。如果您使用了本项目的代码和功能,则视为您已完全理解并接受上述免责声明,并自愿承担使用本项目的一切风险和后果。</b>
<h1>🌟 贡献指南</h1>
<p><strong>欢迎对本项目做出贡献!为了保持代码库的整洁、高效和易于维护,请仔细阅读以下指南,以确保您的贡献能够顺利被接受和整合。</strong></p>
<ul>
<li>在开始开发前,请从 <code>develop</code> 分支拉取最新的代码,以此为基础进行修改;这有助于避免合并冲突并保证您的改动基于最新的项目状态。</li>
<li>如果您的更改涉及多个不相关的功能或问题,请将它们分成多个独立的提交或拉取请求。</li>
<li>每个拉取请求应尽可能专注于单一功能或修复,以便于代码审查和测试。</li>
<li>遵循现有的代码风格;请确保您的代码与项目中已有的代码风格保持一致;建议使用 Ruff 工具保持代码格式规范。</li>
<li>编写可读性强的代码;添加适当的注释帮助他人理解您的意图。</li>
<li>每个提交都应该包含一个清晰、简洁的提交信息,以描述所做的更改。提交信息应遵循以下格式:<code>&lt;类型&gt;: &lt;简短描述&gt;</code></li>
<li>当您准备提交拉取请求时,请优先将它们提交到 <code>develop</code> 分支;这是为了给维护者一个缓冲区,在最终合并到 <code>master</code>
分支之前进行额外的测试和审查。</li>
<li>建议在开发前或遇到疑问时与作者沟通,确保开发方向一致,避免重复劳动或无效提交。</li>
</ul>
<p><strong>参考资料:</strong></p>
<ul>
<li><a href="https://www.contributor-covenant.org/zh-cn/version/2/1/code_of_conduct/">贡献者公约</a></li>
<li><a href="https://opensource.guide/zh-hans/how-to-contribute/">如何为开源做贡献</a></li>
</ul>

# ♥️ 支持项目

<p>如果 <b>DouK-Downloader</b> 对您有帮助,请考虑为它点个 <b>Star</b> ⭐,感谢您的支持!</p>
<table>
<thead>
<tr>
<th align="center">微信(WeChat)</th>
<th align="center">支付宝(Alipay)</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><img src="./docs/微信赞助二维码.png" alt="微信赞助二维码" height="200" width="200"></td>
<td align="center"><img src="./docs/支付宝赞助二维码.png" alt="支付宝赞助二维码" height="200" width="200"></td>
</tr>
</tbody>
</table>
<p>如果您愿意,可以考虑提供资助为 <b>DouK-Downloader</b> 提供额外的支持!</p>

# 💰 项目赞助

## DartNode

[![Powered by DartNode](docs/AD/DartNode_AD.png)](https://dartnode.com "Powered by DartNode - Free VPS for Open Source")

***

## ZMTO

<p><a href="https://www.zmto.com/"><img src="https://console.zmto.com/templates/2019/dist/images/logo_dark.svg" alt="ZMTO"></a></p>
<p><a href="https://www.zmto.com/">ZMTO</a>:一家专业的云基础设施提供商,以可靠的尖端技术与专业支持,提供高效的解决方案,并为符合条件的开源项目提供企业级VPS基础设施,支持开源生态系统的可持续发展与创新。</p>

***

## TikHub

<p><a href="https://tikhub.io/?utm_source=github&utm_medium=readme&utm_campaign=tiktok_downloader&ref=github_joeanamier_tiktokdownloader"><img src="docs/AD/TIKHUB_AD.jpg" alt="TIKHUB" width="458" height="319"></a></p>
<p><a href="https://tikhub.io/?utm_source=github&utm_medium=readme&utm_campaign=tiktok_downloader&ref=github_joeanamier_tiktokdownloader">TikHub API</a> 提供超过 700 个端点,可用于从 14+ 个社交媒体平台获取与分析数据 —— 包括视频、用户、评论、商店、商品与趋势等,一站式完成所有数据访问与分析。</p>
<p>使用 <strong>邀请码</strong>:<code>ZrdH8McC</code> 注册并充值即可获得 <code>$2</code> 额度。</p>

# ✉️ 联系作者

<ul>
<li>作者邮箱:yonglelolu@foxmail.com</li>
<li>作者微信: Downloader_Tools</li>
<li>微信公众号: Downloader Tools</li>
<li><b>Discord 社区</b>: <a href="https://discord.com/invite/ZYtmgKud9Y">点击加入社区</a></li>
<li>QQ 群聊(用于项目交流与摸鱼闲聊): <a href="https://github.com/JoeanAmier/TikTokDownloader/blob/master/docs/QQ%E7%BE%A4%E8%81%8A%E4%BA%8C%E7%BB%B4%E7%A0%81.png">扫码加入群聊</a></li>
</ul>
<p>✨ <b>作者的其他开源项目:</b></p>
<ul>
<li><b>XHS-Downloader(小红书、XiaoHongShu、RedNote)</b>:<a href="https://github.com/JoeanAmier/XHS-Downloader">https://github.com/JoeanAmier/XHS-Downloader</a></li>
<li><b>KS-Downloader(快手、KuaiShou)</b>:<a href="https://github.com/JoeanAmier/KS-Downloader">https://github.com/JoeanAmier/KS-Downloader</a></li>
</ul>
<h1>⭐ Star 趋势</h1>
<p>
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=JoeanAmier/TikTokDownloader&amp;type=Timeline"/>
</p>

# 💡 项目参考

* https://github.com/Johnserf-Seed/f2
* https://github.com/Evil0ctal/Douyin_TikTok_Download_API
* https://github.com/justbeluga/tiktok-web-reverse-engineering
* https://github.com/ihmily/DouyinLiveRecorder
* https://github.com/encode/httpx/
* https://github.com/Textualize/rich
* https://github.com/omnilib/aiosqlite
* https://github.com/Tinche/aiofiles
* https://github.com/pyinstaller/pyinstaller
* https://foss.heptapod.net/openpyxl/openpyxl
* https://github.com/carpedm20/emoji/
* https://github.com/lxml/lxml
* https://ffmpeg.org/ffmpeg-all.html


================================================
FILE: README_EN.md
================================================
<div align="center">
<img src="./static/images/DouK-Downloader.png" alt="DouK-Downloader" height="256" width="256"><br>
<h1>DouK-Downloader</h1>
<p><a href="README.md">简体中文</a> | English</p>
<a href="https://trendshift.io/repositories/6222" target="_blank"><img src="https://trendshift.io/api/badge/repositories/6222" alt="" style="width: 250px; height: 55px;" width="250" height="55"/></a>
<br>
<img alt="GitHub" src="https://img.shields.io/github/license/JoeanAmier/TikTokDownloader?style=flat-square">
<img alt="GitHub forks" src="https://img.shields.io/github/forks/JoeanAmier/TikTokDownloader?style=flat-square&color=55efc4">
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/JoeanAmier/TikTokDownloader?style=flat-square&color=fda7df">
<img alt="GitHub code size in bytes" src="https://img.shields.io/github/languages/code-size/JoeanAmier/TikTokDownloader?style=flat-square&color=a29bfe">
<br>
<img alt="Static Badge" src="https://img.shields.io/badge/Python-3.12-b8e994?style=flat-square&logo=python&labelColor=3dc1d3">
<img alt="GitHub release (with filter)" src="https://img.shields.io/github/v/release/JoeanAmier/TikTokDownloader?style=flat-square&color=48dbfb">
<img src="https://img.shields.io/badge/Sourcery-enabled-884898?style=flat-square&color=1890ff" alt="">
<img alt="Static Badge" src="https://img.shields.io/badge/Docker-badc58?style=flat-square&logo=docker">
<img alt="GitHub all releases" src="https://img.shields.io/github/downloads/JoeanAmier/TikTokDownloader/total?style=flat-square&color=ffdd59">
</div>
<br>
<p>🔥 <b>TikTok Posts/Liked/Mix/Live/Video/Image/Music; DouYin Posts/Liked/Favorites/Collections/Video/Image/LivePhoto/Live/Music/Mix/Comments/Account/Search/Hot Board Data Acquisition Tools:</b> Fully open-source, free data collection and file download tool based on HTTPX module implementation; batch download of DouYin account posts works, liked works, favorites works and collections works; batch download of TikTok account posts works and liked works; download of DouYin linked or TikTok linked works; obtain DouYin live stream push addresses; download DouYin live stream video; obtain TikTok live stream push addresses; download TikTok live stream video; collect DouYin works comments data; batch download of DouYin Mix works; batch download of TikTok Mix works; collect detailed data of DouYin accounts; collect DouYin user/works/live search results; collect DouYin Hot Board data.</p>
<p>⭐ Previous project names: <code>TikTokDownloader</code></p>
<p>📣 This project will undergo code structure refactoring in the future, with the goal of making the code more robust and providing better maintainability and extensibility. If you have any thoughts on project design, implementation methods, or optimization ideas, you are welcome to make suggestions or participate in discussions!</p>
<p>⭐ Due to the author's limited energy, I was unable to update the English document in a timely manner, and the content may have become outdated, partial translation is machine translation, the translation result may be incorrect, Suggest referring to Chinese documentation. If you want to contribute to translation, we warmly welcome you.</p>
<hr>

# 📝 Project Features

<details>
<summary>Function List (Click to Expand)</summary>
<ul>
<li>✅ Download DouYin video/image</li>
<li>✅ Download DouYin live photo</li>
<li>✅ Download the highest quality video file</li>
<li>✅ Download TikTok video source files</li>
<li>✅ Download TikTok video/image</li>
<li>✅ Download of DouYin account posts/liked/favorites works</li>
<li>✅ Download of TikTok account posts/liked works</li>
<li>✅ Collect detailed data from DouYin/TikTok</li>
<li>✅ Batch download of linked works</li>
<li>✅ Batch download of works from multiple accounts</li>
<li>✅ Automatically skip already downloaded files</li>
<li>✅ Persistently save collected data</li>
<li>✅ Support CSV/XLSX/SQLite format for saving data</li>
<li>✅ Download dynamic/static cover images</li>
<li>✅ Obtain DouYin live stream push addresses</li>
<li>✅ Obtain TikTok live stream push addresses</li>
<li>✅ Use ffmpeg to download live video</li>
<li>✅ Web UI interaction interface</li>
<li>✅ Collect comments data from DouYin works</li>
<li>✅ Batch download of DouYin Mix works</li>
<li>✅ Batch download of TikTok Mix works</li>
<li>✅ Record statistics such as likes and favorites</li>
<li>✅ Filter works based on publication time</li>
<li>✅ Support incremental downloading of account works</li>
<li>✅ Support data Collections using proxies</li>
<li>✅ Support remote access via LAN</li>
<li>✅ Collect detailed data from DouYin accounts</li>
<li>✅ Update statistics of works</li>
<li>✅ Support custom account/mix mark</li>
<li>✅ Automatically update account nickname/mark</li>
<li>✅ Deploy to private servers</li>
<li>✅ Deploy to public servers</li>
<li>✅ Collect DouYin search data</li>
<li>✅ Collect DouYin hot board data</li>
<li>✅ Record IDs of already downloaded works</li>
<li>☑️ <del>Scan QR code to log in and obtain Cookies</del></li>
<li>✅ Obtain Cookies from browsers</li>
<li>✅ Support Web API calls</li>
<li>✅ Support multithreaded downloading of works</li>
<li>✅ File integrity processing mechanism</li>
<li>✅ Custom rules for filtering works</li>
<li>✅ Archive and save works files by folder</li>
<li>✅ Customize file size limit</li>
<li>✅ Support resume downloading of files from breakpoints</li>
<li>✅ Monitor clipboard links to download works</li>
</ul>
</details>

# 💻 Program Screenshot

<p><a href="https://www.bilibili.com/video/BV1d7eAzTEFs/">Watch Demo on Bilibili</a>; <a href="https://youtu.be/yMU-RWl55hg">Watch Demo on YouTube</a></p>

## Terminal interaction mode

<p>It is recommended to manage accounts through configuration files. For more information, please refer to the <a href="https://github.com/JoeanAmier/TikTokDownloader/wiki/Documentation">documentation</a></p>

![终端模式截图](docs/screenshot/终端交互模式截图EN1.png)
*****
![终端模式截图](docs/screenshot/终端交互模式截图EN2.png)
*****
![终端模式截图](docs/screenshot/终端交互模式截图EN3.png)

## Web UI interaction mode

> **The project code has been refactored; the code for this mode has not yet been updated. It will be reopened after
future development is completed!**

## Web API mode

![WebAPI模式截图](docs/screenshot/WebAPI模式截图EN1.png)
*****
![WebAPI模式截图](docs/screenshot/WebAPI模式截图EN2.png)

> **After starting this mode, Open http://127.0.0.1:5555/docs or http://127.0.0.1:5555/redoc to access the automatically
generated documentation!**

### API call example code

```python
from httpx import post
from rich import print


def demo():
    headers = {"token": ""}
    data = {
        "detail_id": "0123456789",
        "pages": 2,
    }
    api = "http://127.0.0.1:5555/douyin/comment"
    response = post(api, json=data, headers=headers)
    print(response.json())


demo()
```

# 📋 Project Instructions

## Quick Start

<p>⭐ Mac OS and Windows 10 and above users can go to <a href="https://github.com/JoeanAmier/TikTokDownloader/releases/latest">Releases</a> or <a href="https://github.com/JoeanAmier/TikTokDownloader/actions">Actions</a> to download the compiled program, ready to use!</p>
<p>⭐ This project includes GitHub Actions for automatic building executable files. Users can use GitHub Actions to build the latest source code into executable files at any time!</p>
<p>⭐ For the automatic building executable files tutorial, please refer to the <code>Build of Executable File Guide</code> section of this document. If you need a more detailed step-by-step tutorial with illustrations, please <a href="https://mp.weixin.qq.com/s/TorfoZKkf4-x8IBNLImNuw">check out this article</a>!</p>
<p><strong>Note: Due to the macOS platform's executable file <code>main</code> not being code-signed, it will be restricted by system security measures on first run. Please execute the command <code>xattr -cr project_folder_path</code> in the terminal to remove the security flag, after which it can run normally.</strong></p>
<hr>
<ol>
<li><b>Run the executable file</b> or <b>configure the environment to run</b> (choose one of the two)
<ol><b>Run the executable file</b>
<li>Download the executable file compressed file built by <a href="https://github.com/JoeanAmier/TikTokDownloader/releases/latest">Releases</a> or Actions.</li>
<li>After extracting, open the program folder and double-click to run <code>main</code>.</li>
</ol>
<ol><b>Configure the environment to run</b>

[//]: # (<li>Install Python interpreter version not lower than <code>3.12</code></li>)
<li>Install the <a href="https://www.python.org/">Python</a> interpreter version <code>3.12</code></li>
<li>Download the latest source code or the source code released in <a href="https://github.com/JoeanAmier/TikTokDownloader/releases/latest">Releases</a> to your local machine</li>
<ol><b>Install project dependencies using pip</b>
<li>Run the command <code>python -m venv venv</code> to create a virtual environment (optional)</li>
<li>Run the command <code>.\venv\Scripts\activate.ps1</code> or <code>venv\Scripts\activate</code> to activate the virtual environment (optional)</li>
<li>Run the command <code>pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt</code> to install the required modules for the program</li>
<li>Run the command <code>python .\main.py</code> or <code>python main.py</code> to start DouK-Downloader</li>
</ol>
<ol><b>Install project dependencies using uv (recommended)</b>
<li>Run the command <code>uv sync --no-dev</code> to synchronize environment dependencies</li>
<li>Run the command <code>uv run main.py</code> to start DouK-Downloader</li>
</ol>
</ol>
</li>
<li>Read the disclaimer of DouK-Downloader and enter content according to the prompt.</li>
<li>Write Cookie Information into Configuration File 
<ol><b>Read Cookie from Clipboard(Recommended)</b>
<li>Refer to the <a href="https://github.com/JoeanAmier/TikTokDownloader/blob/master/docs/Cookie%E8%8E%B7%E5%8F%96%E6%95%99%E7%A8%8B.md">Cookie Extraction Tutorial</a>, copy the required Cookie to the clipboard</li>
<li>Select the <code>Read Cookie from Clipboard</code> option, the program will automatically read the Cookie from the clipboard and write it into the configuration file</li>
</ol>
<ol><b>Read Cookie from Browser</b>
<li>Select the <code>Read Cookie from Browser</code> option, then follow the prompts to input the browser type or its corresponding number</li>
</ol>
<ol><b><del>Obtain Cookie via QR Code Login</del> (No longer valid)</b>
<li><del>Select the <code>Obtain Cookie via QR Code Login</code> option, the program will display a login QR code image and open it with the default application</del></li>
<li><del>Use the TikTok app to scan the QR code and log in</del></li>
<li><del>Follow the prompts, the program will automatically write the Cookie into the configuration file</del></li>
</ol>
</li>
<li>Return to the program interface, sequentially select <code>Terminal interactive mode</code> -> <code>Batch download link works (general)</code> -> <code>Manually enter the link of the works to be collected</code>.</li>
<li>Input the DouYin works link to download the works file (the TikTok platform requires more initial setup, please refer to the documentation for details).</li>
<li>For more detailed instructions, please see <b><a href="https://github.com/JoeanAmier/TikTokDownloader/wiki/Documentation">Project Documentation</a></b>.</li>
</ol>
<p>⭐ It is recommended to use <a href="https://learn.microsoft.com/zh-cn/windows/terminal/install">Windows Terminal</a> (the default terminal that comes with Windows 11).</p>

### Docker Container

<ol>
<li>Get the image</li>
<ul>
<li>Method 1: Build the image using the <code>Dockerfile</code>.</li>
<li>Method 2: Pull the image using the command <code>docker pull joeanamier/tiktok-downloader</code>.</li>
<li>Method 3: Pull the image using the command <code>docker pull ghcr.io/joeanamier/tiktok-downloader</code>.</li>
</ul>
<li>Create the container: <code>docker run --name ContainerName(optional) -p HostPort:5555 -v tiktok_downloader_volume:/app/Volume -it &lt;image name&gt;</code>.</li>
<br><b>Note:</b> The <code>&lt;image name&gt;</code> here must be consistent with the image name you used in the first step (<code>joeanamier/tiktok-downloader</code> or <code>ghcr.io/joeanamier/tiktok-downloader</code>)
<li>Run the container
<ul>
<li>Start the container: <code>docker start -i container name/container ID</code>.</li>
<li>Restart the container: <code>docker restart -i container name/container ID</code>.</li>
</ul>
</li>
</ol>
<p>Docker containers cannot directly access the host machine's file system, and some features may be unavailable, for example: <code>Get Cookie from Browser</code>; if there are any other issues, please report!</p>
<hr>

## About Cookie

[Click to view Cookie tutorial](https://github.com/JoeanAmier/TikTokDownloader/blob/master/docs/Cookie%E8%8E%B7%E5%8F%96%E6%95%99%E7%A8%8B.md)

> * Cookie only needs to be re-written to the configuration file after it expires, and not every time the program is
    run.
>
> * The Cookie can affect the resolution of the video files downloaded from the DouYin platform. If you are unable to
    download high-resolution video files, please try updating the Cookie!
>
> * When the program fails to obtain data, you can try updating the Cookie or using a Cookie that is already logged in!

<hr>

## Other Instructions

<ul>
<li>When the program prompts the user for input, pressing Enter directly will return to the previous menu, and inputting <code>Q</code> or <code>q</code> will end the program's execution.</li>
<li>Since fetching data for liked and favorites works of an account only returns the publication dates of those works, not the dates of the actions (liking or favouring), the program needs to retrieve all liked and favorites works data before performing date filtering. If there are a large number of works, this may take a considerable amount of time. The number of requests can be controlled via the <code>max_pages</code> parameter.</li>
<li>To obtain data for posts made by a private account, a logged-in Cookie is required, and the logged-in account must follow the private account.</li>
<li>When batch downloading account posts works or mix works, if the corresponding nickname or mark parameter changes, the program will automatically update the nickname and mark parameter in the file names of the downloaded works.</li>
<li>When downloading files, the program first downloads them to a temporary folder and then moves them to the storage folder upon completion. The temporary folder will be emptied when the program ends.</li>
<li>The <code>Batch Download Favorites Works Mode</code> currently only supports downloading Favorites works for the account corresponding to the currently logged-in Cookie and does not support multiple accounts.</li>
<li>If you want the program to use a proxy to request data, you must set the <code>proxy</code> parameter in <code>settings.json</code>; otherwise, the program will not use a proxy.</li>
<li>If your computer does not have a suitable program for editing JSON files, we recommend using the <a href="https://www.toolhelper.cn/JSON/JSONFormat">Online Tool</a> to edit the configuration file content, after modification, the software needs to be restarted to take effect.</li>
<li>When the program prompts the user to input content or links, please be careful to avoid including newline characters, as this may cause unexpected issues.</li>
<li>This project does not support downloading paid works. Please do not report any issues related to downloading paid works.</li>
<li>On Windows systems, the program needs to be run as an administrator to read Cookies from Chromium, Chrome, and Edge browsers.</li>
<li>This project has not been optimized for running multiple instances of the program. If you need to run multiple instances, please copy the entire project folder to avoid unexpected issues.</li>
<li>During program execution, if you need to terminate the program or <code>ffmpeg</code>, please press <code>Ctrl + C</code> to stop the process. Do not click the close button on the terminal window directly.</li>
</ul>
<h2>Build of Executable File Guide</h2>
<details>
<summary>Build of Executable File Guide (Click to Expand)</summary>

This guide will walk you through forking this repository and executing GitHub Actions to automatically build and package
the program based on the latest source code!

---

### Steps to Use

#### 1. Fork the Repository

1. Click the **Fork** button at the top right of the project repository to fork it to your personal GitHub account
2. Your forked repository address will look like this: `https://github.com/your-username/this-repo`

---

#### 2. Enable GitHub Actions

1. Go to the page of your forked repository
2. Click the **Settings** tab at the top
3. Click the **Actions** tab on the right
4. Click the **General** option
5. Under **Actions permissions**, select **Allow all actions and reusable workflows** and click the **Save** button

---

#### 3. Manually Trigger the Build Process

1. In your forked repository, click the **Actions** tab at the top
2. Find the workflow named **构建可执行文件**
3. Click the **Run workflow** button on the right:
    - Select the **master** or **develop** branch
    - Click **Run workflow**

---

#### 4. Check the Build Progress

1. On the **Actions** page, you can see the execution records of the triggered workflow
2. Click on the run record to view detailed logs to check the build progress and status

---

#### 5. Download the Build Result

1. Once the build is complete, go to the corresponding run record page
2. In the **Artifacts** section at the bottom of the page, you will see the built result file
3. Click to download and save it to your local machine to get the built program

---

### Notes

1. **Resource Usage**:
    - GitHub provides free build environments for Actions, with a monthly usage limit (2000 minutes) for free-tier
      users

2. **Code Modifications**:
    - You are free to modify the code in your forked repository to customize the build process
    - After making changes, you can trigger the build process again to get your customized version

3. **Stay in Sync with the Main Repository**:
    - If the main repository is updated with new code or workflows, it is recommended that you periodically sync your
      forked repository to get the latest features and fixes

---

### Frequently Asked Questions

#### Q1: Why can't I trigger the workflow?

A: Please ensure that you have followed the steps to **Enable Actions**. Otherwise, GitHub will prevent the workflow
from running

#### Q2: What should I do if the build process fails?

A:

- Check the run logs to understand the cause of the failure
- Ensure there are no syntax errors or dependency issues in the code
- If the problem persists, please open an issue on
  the [Issues page](https://github.com/JoeanAmier/TikTokDownloader/issues)

#### Q3: Can I directly use the Actions from the main repository?

A: Due to permission restrictions, you cannot directly trigger Actions from the main repository. Please use the forked
repository to execute the build process

</details>

## Program Update

<p><strong>Method 1:</strong> Download and extract the files, then copy the old version of the <code>_internal\Volume</code> folder into the new version's <code>_internal</code> folder.</p>
<p><strong>Method 2:</strong> Download and extract the files (do not run the program), then copy all files and directly overwrite the old version.</p>

# ⚠️ Disclaimer

<ol>
<li>The user's use of this project is entirely at their own discretion and responsibility. The author assumes no liability for any losses, claims, or risks arising from the user's use of this project.</li>
<li>The code and functionalities provided by the author of this project are based on current knowledge and technological developments. The author strives to ensure the correctness and security of the code according to existing technical capabilities but does not guarantee that the code is entirely free of errors or defects.</li>
<li>All third-party libraries, plugins, or services relied upon by this project follow their respective open-source or commercial licenses. Users must review and comply with those license agreements. The author assumes no responsibility for the stability, security, or compliance of third-party components.</li>
<li>Users must strictly comply with the requirements of the <a href="https://github.com/JoeanAmier/TikTokDownloader/blob/master/LICENSE">GNU General Public License v3.0</a> when using this project and properly indicate that the code was used under the <a href="https://github.com/JoeanAmier/TikTokDownloader/blob/master/LICENSE">GNU General Public License v3.0</a>.</li>
<li>When using the code and features of this project, users must independently research relevant laws and regulations and ensure their actions are legal and compliant. Any legal liabilities or risks arising from violations of laws and regulations shall be borne solely by the user.</li>
<li>Users must not use this tool to engage in any activities that infringe intellectual property rights, including but not limited to downloading or distributing copyright-protected content without authorization. The developers do not participate in, support, or endorse any unauthorized acquisition or distribution of illegal content.</li>
<li>This project assumes no responsibility for the compliance of any data processing activities (including collection, storage, and transmission) conducted by users. Users must comply with relevant laws and regulations and ensure that their processing activities are lawful and proper. Legal liabilities resulting from non-compliant operations shall be borne by the user.</li>
<li>Under no circumstances may users associate the author, contributors, or other related parties of this project with their usage of the project, nor may they hold these parties responsible for any loss or damage arising from such usage.</li>
<li>The author of this project will not provide a paid version of the DouK-Downloader project, nor will they offer any commercial services related to the DouK-Downloader project.</li>
<li>Any secondary development, modification, or compilation based on this project is unrelated to the original author. The original author assumes no liability for any consequences resulting from such secondary development. Users bear full responsibility for all outcomes arising from such modifications.</li>
<li>This project grants no patent licenses; if the use of this project leads to patent disputes or infringement, the user bears all associated risks and responsibilities. Without written authorization from the author or rights holder, users may not use this project for any commercial promotion, marketing, or re-licensing.</li>
<li>The author reserves the right to terminate service to any user who violates this disclaimer at any time and may require them to destroy all obtained code and derivative works.</li>
<li>The author reserves the right to update this disclaimer at any time without prior notice. Continued use of the project constitutes acceptance of the revised terms.</li>
</ol>
<b>Before using the code and functionalities of this project, please carefully consider and accept the above disclaimer. If you have any questions or disagree with the statement, please do not use the code and functionalities of this project. If you use the code and functionalities of this project, it is considered that you fully understand and accept the above disclaimer, and willingly assume all risks and consequences associated with the use of this project.</b>
<h1>🌟 Contribution Guidelines</h1>
<p><strong>Welcome to contributing to this project! To keep the codebase clean, efficient, and easy to maintain, please read the following guidelines carefully to ensure that your contributions can be accepted and integrated smoothly.</strong></p>
<ul>
<li>Before starting development, please pull the latest code from the <code>develop</code> branch as the basis for your modifications; this helps avoid merge conflicts and ensures your changes are based on the latest state of the project.</li>
<li>If your changes involve multiple unrelated features or issues, please split them into several independent commits or pull requests.</li>
<li>Each pull request should focus on a single feature or fix as much as possible, to facilitate code review and testing.</li>
<li>Follow the existing coding style; make sure your code is consistent with the style already present in the project; please use the Ruff tool to maintain code formatting standards.</li>
<li>Write code that is easy to read; add appropriate annotation to help others understand your intentions.</li>
<li>Each commit should include a clear and concise commit message describing the changes made. The commit message should follow this format: <code>&lt;type&gt;: &lt;short description&gt;</code></li>
<li>When you are ready to submit a pull request, please prioritize submitting them to the <code>develop</code> branch; this provides maintainers with a buffer zone for additional testing and review before final merging into the <code>master</code> branch.</li>
<li>It is recommended to communicate with the author before starting development or when encountering questions to ensure alignment in direction and avoid redundant efforts or unnecessary commits.</li>
</ul>
<p><strong>Reference materials:</strong></p>
<ul>
<li><a href="https://www.contributor-covenant.org/version/2/1/code_of_conduct/">Contributor Covenant</a></li>
<li><a href="https://opensource.guide/how-to-contribute/">How to Contribute to Open Source</a></li>
</ul>

# ♥️ Support the Project

<p>If <b>DouK-Downloader</b> has been helpful to you, please consider giving it a <b>Star</b> ⭐. Your support is greatly appreciated!</p>
<table>
<thead>
<tr>
<th align="center">微信(WeChat)</th>
<th align="center">支付宝(Alipay)</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><img src="./docs/微信赞助二维码.png" alt="微信赞助二维码" height="200" width="200"></td>
<td align="center"><img src="./docs/支付宝赞助二维码.png" alt="支付宝赞助二维码" height="200" width="200"></td>
</tr>
</tbody>
</table>
<p>If you're willing, consider making a contribution to provide additional support for <b>DouK-Downloader</b>!</p>

# 💰 Project Sponsorship

## DartNode

[![Powered by DartNode](docs/AD/DartNode_AD.png)](https://dartnode.com "Powered by DartNode - Free VPS for Open Source")

***

## ZMTO

<p><a href="https://www.zmto.com/"><img src="https://console.zmto.com/templates/2019/dist/images/logo_dark.svg" alt="ZMTO"></a></p>
<p><a href="https://www.zmto.com/">ZMTO</a>: A professional cloud infrastructure provider offering sophisticated solutions with reliable technology and expert support. We also empower qualified open source initiatives with enterprise-grade VPS infrastructure, driving sustainable development and innovation in the open source ecosystem. </p>

***

## TikHub

<p><a href="https://tikhub.io/?utm_source=github&utm_medium=readme&utm_campaign=tiktok_downloader&ref=github_joeanamier_tiktokdownloader"><img src="docs/AD/TIKHUB_AD.jpg" alt="TIKHUB" width="458" height="319"></a></p>
<p><a href="https://tikhub.io/?utm_source=github&utm_medium=readme&utm_campaign=tiktok_downloader&ref=github_joeanamier_tiktokdownloader">TikHub API</a> offers over 700 endpoints to retrieve and analyze data from 14+ social media platforms—including videos, users, comments, stores, products, trends, and more—enabling one-stop access and analysis of all your data.</p>
<p>Use <strong>invitation code</strong>: <code>ZrdH8McC</code> to register and recharge to get <code>$2</code> credit.</p>

# ✉️ Contact the Author

<ul>
<li>Author's Email: yonglelolu@foxmail.com</li>
<li>Author's WeChat: Downloader_Tools</li>
<li>Official WeChat Account: Downloader Tools</li>
<li><b>Discord Community</b>: <a href="https://discord.com/invite/ZYtmgKud9Y">Click to join the community</a></li>
</ul>
<p>✨ <b>The author's other open-source projects:</b></p>
<ul>
<li><b>XHS-Downloader(小红书、XiaoHongShu、RedNote)</b>:<a href="https://github.com/JoeanAmier/XHS-Downloader">https://github.com/JoeanAmier/XHS-Downloader</a></li>
<li><b>KS-Downloader(快手、KuaiShou)</b>:<a href="https://github.com/JoeanAmier/KS-Downloader">https://github.com/JoeanAmier/KS-Downloader</a></li>
</ul>
<h1>⭐ Star History</h1>
<p>
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=JoeanAmier/TikTokDownloader&amp;type=Timeline"/>
</p>

# 💡 Project References

* https://github.com/Johnserf-Seed/f2
* https://github.com/Evil0ctal/Douyin_TikTok_Download_API
* https://github.com/justbeluga/tiktok-web-reverse-engineering
* https://github.com/ihmily/DouyinLiveRecorder
* https://github.com/encode/httpx/
* https://github.com/Textualize/rich
* https://github.com/omnilib/aiosqlite
* https://github.com/Tinche/aiofiles
* https://github.com/pyinstaller/pyinstaller
* https://foss.heptapod.net/openpyxl/openpyxl
* https://github.com/carpedm20/emoji/
* https://github.com/lxml/lxml
* https://ffmpeg.org/ffmpeg-all.html


================================================
FILE: docs/Cookie获取教程.md
================================================
# Cookie 获取教程

本教程仅演示部分能够获取所需 `Cookie` 的方法,仍有其他方法能够获取所需 `Cookie`;本教程使用的浏览器为 `Microsoft Edge`
,部分浏览器的开发人员工具可能不支持中文语言。

**方法一\(推荐\):**

1. 打开浏览器\(可选无痕模式启动\),访问`https://www.douyin.com/`
2. 登录抖音账号\(可跳过\)
3. 按 `F12` 打开开发人员工具
4. 选择 `网络` 选项卡
5. 勾选 `保留日志`
6. 在 `筛选器` 输入框输入 `cookie-name:odin_tt`
7. 点击加载任意一个作品的评论区
8. 在开发人员工具窗口选择任意一个数据包\(如果无数据包,重复步骤7\)
9. 全选并复制 `Cookie` 的值
10. 运行 `main.py` ,根据提示写入 `Cookie`

**截图示例:**

<img src="screenshot/Cookie获取教程1.png" alt="开发人员工具">

**方法二\(不适用本项目\):**

1. 打开浏览器\(可选无痕模式启动\),访问`https://www.douyin.com/`
2. 登录抖音账号\(可跳过\)
3. 按 `F12` 打开开发人员工具
4. 选择 `控制台` 选项卡
5. 输入 `document.cookie` 后回车确认
6. 检查 `Cookie` 是否包含 `passport_csrf_token` 和 `odin_tt` 字段
7. 如果未包含所需字段,尝试刷新网页或者点击加载任意一个作品的评论区,回到步骤5
8. 全选并复制 `Cookie` 的值
9. 运行 `main.py` ,根据提示写入 `Cookie`

**截图示例:**

<img src="screenshot/Cookie获取教程2.png" alt="开发人员工具">

# device_id 参数

`device_id` 参数获取方法与 Cookie 类似。

<img src="screenshot/device_id获取示例图.png" alt="开发人员工具">


================================================
FILE: docs/DouK-Downloader文档.md
================================================
<div align="center">
<img src="https://github.com/JoeanAmier/TikTokDownloader/blob/master/static/images/DouK-Downloader.png" alt="DouK-Downloader" height="256" width="256"><br>
<h1>DouK-Downloader 项目文档</h1>
<a href="https://trendshift.io/repositories/6222" target="_blank"><img src="https://trendshift.io/api/badge/repositories/6222" alt="" style="width: 250px; height: 55px;" width="250" height="55"/></a>
<br>
<img alt="GitHub" src="https://img.shields.io/github/license/JoeanAmier/TikTokDownloader?style=flat-square">
<img alt="GitHub forks" src="https://img.shields.io/github/forks/JoeanAmier/TikTokDownloader?style=flat-square&color=55efc4">
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/JoeanAmier/TikTokDownloader?style=flat-square&color=fda7df">
<img alt="GitHub code size in bytes" src="https://img.shields.io/github/languages/code-size/JoeanAmier/TikTokDownloader?style=flat-square&color=a29bfe">
<br>
<img alt="Static Badge" src="https://img.shields.io/badge/Python-3.12-b8e994?style=flat-square&logo=python&labelColor=3dc1d3">
<img alt="GitHub release (with filter)" src="https://img.shields.io/github/v/release/JoeanAmier/TikTokDownloader?style=flat-square&color=48dbfb">
<img src="https://img.shields.io/badge/Sourcery-enabled-884898?style=flat-square&color=1890ff" alt="">
<img alt="Static Badge" src="https://img.shields.io/badge/Docker-badc58?style=flat-square&logo=docker">
<img alt="GitHub all releases" src="https://img.shields.io/github/downloads/JoeanAmier/TikTokDownloader/total?style=flat-square&color=ffdd59">
</div>
<br>
<p>🔥 <b>TikTok 发布/喜欢/合辑/直播/视频/图集/音乐;抖音发布/喜欢/收藏/收藏夹/视频/图集/实况/直播/音乐/合集/评论/账号/搜索/热榜数据采集工具:</b>完全开源,基于 HTTPX 模块实现的免费数据采集和文件下载工具;批量下载抖音账号发布、喜欢、收藏、收藏夹作品;批量下载 TikTok 账号发布、喜欢作品;下载抖音链接或 TikTok 链接作品;获取抖音直播拉流地址;下载抖音直播视频;获取 TikTok 直播拉流地址;下载 TikTok 直播视频;采集抖音作品评论数据;批量下载抖音合集作品;批量下载 TikTok 合辑作品;采集抖音账号详细数据;采集抖音用户 / 作品 / 直播搜索结果;采集抖音热榜数据。</p>
<p>⭐ <b>项目版本:<code>5.8 Beta</code>;文档更新日期:<code>2026/2/28</code></b></p>
<p>⭐ <b>项目文档正在完善,如果发现任何错误或描述模糊之处,请告知作者以便改进!本项目历史名称:<code>TikTokDownloader</code></b></p>
<p>⭐ Due to the author’s limited time and energy, the complete English documentation for this project is not yet available. If you wish to read the full documentation, we recommend using AI translation tools to assist your understanding. If you would like to contribute to the translation, your help is warmly welcomed.</p>
<hr>
<h1>快速入门</h1>
<p>⭐ 本项目包含手动构建可执行文件的 GitHub Actions,使用者可以随时使用 GitHub Actions 将最新源码构建为可执行文件!</p>
<p>⭐ 自动构建可执行文件教程请查阅本文档的 <code>构建可执行文件指南</code> 部分;如果需要更加详细的图文教程,请 <a href="https://mp.weixin.qq.com/s/TorfoZKkf4-x8IBNLImNuw">查阅文章</a>!</p>
<p><strong>注意:由于 Mac OS 平台的可执行文件 <code>main</code> 未经过代码签名,首次运行时会受到系统安全限制。请先在终端执行 <code>xattr -cr main.app</code> 命令移除安全标记,执行一次后即可正常运行。</strong></p>
<ol>
<li><b>运行可执行文件</b> 或者 <b>配置环境运行</b>
<ol><b>运行可执行文件</b>
<li>下载 <a href="https://github.com/JoeanAmier/TikTokDownloader/releases/latest">Releases</a> 或者 Actions 构建的可执行文件压缩包</li>
<li>解压后打开程序文件夹,双击运行 <code>main</code></li>
</ol>
<ol><b>配置环境运行</b>

[//]: # (<li>安装不低于 <code>3.12</code> 版本的 <a href="https://www.python.org/">Python</a> 解释器</li>)
<li>安装 <code>3.12</code> 版本的 <a href="https://www.python.org/">Python</a> 解释器</li>
<li>下载最新的源码或 <a href="https://github.com/JoeanAmier/TikTokDownloader/releases/latest">Releases</a> 发布的源码至本地</li>
<li>运行 <code>python -m venv venv</code> 命令创建虚拟环境(可选)</li>
<li>运行 <code>.\venv\Scripts\activate.ps1</code> 或者 <code>venv\Scripts\activate</code> 命令激活虚拟环境(可选)</li>
<li>运行 <code>pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt</code> 命令安装程序所需模块</li>
<li>运行 <code>python .\main.py</code> 或者 <code>python main.py</code> 命令启动 DouK-Downloader</li>
</ol>
</li>
<li>阅读 DouK-Downloader 的免责声明,根据提示输入内容</li>
<li>将 Cookie 信息写入配置文件
<ol><b>从剪贴板读取 Cookie(推荐)</b>
<li>参考 <a href="https://github.com/JoeanAmier/TikTokDownloader/blob/master/docs/Cookie%E8%8E%B7%E5%8F%96%E6%95%99%E7%A8%8B.md">Cookie 提取教程</a>,复制所需 Cookie 至剪贴板</li>
<li>选择 <code>从剪贴板读取 Cookie</code> 选项,程序会自动读取剪贴板的 Cookie 并写入配置文件</li>
</ol>
<ol><b>从浏览器读取 Cookie</b>
<li>选择 <code>从浏览器读取 Cookie</code> 选项,按照提示输入浏览器类型或序号</li>
</ol>
<ol><b><del>扫码登录获取 Cookie</del>(失效)</b>
<li><del>选择 <code>扫码登录获取 Cookie</code> 选项,程序会显示登录二维码图片,并使用默认应用打开图片</del></li>
<li><del>使用抖音 APP 扫描二维码并登录账号</del></li>
<li><del>按照提示操作,程序会自动将 Cookie 写入配置文件</del></li>
</ol>
</li>
<li>返回程序界面,依次选择 <code>终端交互模式</code> -> <code>批量下载链接作品(抖音)</code> -> <code>手动输入待采集的作品链接</code></li>
<li>输入抖音作品链接即可下载作品文件</li>
</ol>
<p><b>TikTok 平台功能需要额外设置配置文件 <code>browser_info_tiktok</code> 的 <code>device_id</code> 参数,否则 TikTok 平台功能可能无法正常使用!参数获取方式与 Cookie 类似,详见 <a href="https://github.com/JoeanAmier/TikTokDownloader/blob/master/docs/Cookie%E8%8E%B7%E5%8F%96%E6%95%99%E7%A8%8B.md">Cookie 获取教程</a></b></p>
<h2>Docker 容器</h2>
<ol>
<li>获取镜像</li>
<ul>
<li>方式一:使用 <code>Dockerfile</code> 文件构建镜像</li>
<li>方式二:使用 <code>docker pull joeanamier/tiktok-downloader</code> 命令拉取镜像</li>
<li>方式三:使用 <code>docker pull ghcr.io/joeanamier/tiktok-downloader</code> 命令拉取镜像</li>
</ul>
<li>创建容器:<code>docker run --name 容器名称(可选) -p 主机端口号:5555 -v tiktok_downloader_volume:/app/Volume -it &lt;镜像名称&gt;</code>
</li>
<br><b>注意:</b>此处的 <code>&lt;镜像名称&gt;</code> 需与您在第一步中使用的镜像名称保持一致(例如 <code>joeanamier/tiktok-downloader</code> 或 <code>ghcr.io/joeanamier/tiktok-downloader</code>)
<li>运行容器
<ul>
<li>启动容器:<code>docker start -i 容器名称/容器 ID</code></li>
<li>重启容器:<code>docker restart -i 容器名称/容器 ID</code></li>
</ul>
</li>
</ol>
<p>Docker 容器无法直接访问宿主机的文件系统,部分功能不可用,例如:<code>从浏览器读取 Cookie</code>;其他功能如有异常请反馈!</p>
<h1>Cookie 说明</h1>
<p><a href="https://github.com/JoeanAmier/TikTokDownloader/blob/master/docs/Cookie%E8%8E%B7%E5%8F%96%E6%95%99%E7%A8%8B.md">点击查看 Cookie 获取教程</a>;无效或失效的 Cookie 会导致程序获取数据失败!</p>
<ul>
<li>Cookie 仅需在失效后重新写入配置文件,并非每次运行程序都要写入配置文件!</li>
<li><p>Cookie 会影响下载的视频文件分辨率,如果无法下载最高分辨率的视频文件,请尝试更新 Cookie!</li>
<li>程序获取数据失败时,可以尝试更新 Cookie 或者使用已登录的 Cookie!</li>
</ul>
<h1>入门说明</h1>
<h2>关于终端</h2>
<p>⭐ 推荐使用 <a href="https://learn.microsoft.com/zh-cn/windows/terminal/install">Windows 终端</a>(Windows 11 自带默认终端)运行程序以便获得最佳彩色交互显示效果!</p>
<h2>链接类型</h2>
<ul>
<li>完整链接:使用浏览器打开抖音或 TikTok 链接时,地址栏所显示的 URL 地址。</li>
<li>分享链接:点击 APP 或网页版的分享按钮得到的 URL 地址,抖音平台以 <code>https://v.</code> 开头,掺杂中文和其他字符;TikTok
平台以 <code>https://vm.</code> 或 <code>https://vt.</code> 开头,不掺杂其他字符;使用时<b>不需要</b>手动去除中文和其他字符,程序会自动提取 URL 链接。</li>
</ul>
<h2>数据储存</h2>
<ul>
<li>项目支持使用 <code>CSV</code>、<code>XLSX</code>、<code>SQLite</code> 格式文件储存采集数据。</li>
<li>配置文件 <code>settings.json</code> 的 <code>storage_format</code> 参数可设置数据储存格式类型,如果不设置该参数,程序不会储存任何数据至文件。</li>
<li><code>采集作品评论数据</code>、<code>采集账号详细数据</code>、<code>采集搜索结果数据</code>、<code>采集抖音热榜数据</code> 模式必须设置 <code>storage_format</code> 参数才能正常使用。</li>
<li>程序所有数据均储存至配置文件 <code>root</code> 参数路径下的 <code>Data</code> 文件夹。</li>
</ul>
<h2>文本文档</h2>
<p>项目部分功能支持从文本文档(TXT)读取链接,如需使用,请在计算机任意路径创建一个空白文本文档,然后编辑文件内容,每行输入单个链接,编辑完成后保存文件。</p>
<p>文本文档编码:UTF-8</p>
<h3>文本文档内容示例</h3>

```text
https://www.douyin.com/user/abcd?vid=123456789
https://www.douyin.com/search/key?modal_id=123456789
https://www.douyin.com/video/123456789
https://www.douyin.com/note/123456789
```

<h2>直播下载</h2>
<p><code>获取直播拉流地址</code> 功能需要调用 <code>ffmpeg</code> 下载直播文件;程序会优先调用系统环境的 <code>ffmpeg</code>,其次调用 <code>ffmpeg</code> 参数指定的 <code>ffmpeg</code>,如果 <code>ffmpeg</code> 不可用,程序将不支持直播下载!</p>
<p>建议前往 <a href="https://ffmpeg.org/download.html">官方网站</a> 或者 <a href="https://github.com/BtbN/FFmpeg-Builds">FFmpeg-Builds</a> 获取 <code>ffmpeg</code> 程序!</p>
<p>项目开发时所用的 FFmpeg 版本信息如下,不同版本的 FFmpeg 可能会有差异;若功能异常,请向作者反馈!</p>
<pre>
ffmpeg version n7.1.1-6-g48c0f071d4-20250405 Copyright (c) 2000-2025 the FFmpeg developers
built with gcc 14.2.0 (crosstool-NG 1.27.0.18_7458341)
</pre>
<h2>功能汇总</h2>
<table>
<thead>
<tr>
<th align="center">程序功能</th>
<th align="center">功能类型</th>
</tr>
</thead>
<tbody><tr>
<td align="center">批量下载账号作品(发布、喜欢)</td>
<td align="center">文件下载, 数据采集</td>
</tr>
<tr>
<td align="center">批量下载链接作品</td>
<td align="center">文件下载, 数据采集</td>
</tr>
<tr>
<td align="center">获取直播拉流地址</td>
<td align="center">文件下载, 数据采集</td>
</tr>
<tr>
<td align="center">采集作品评论数据</td>
<td align="center">数据采集</td>
</tr>
<tr>
<td align="center">批量下载合集作品</td>
<td align="center">文件下载, 数据采集</td>
</tr>
<tr>
<td align="center">采集账号详细数据</td>
<td align="center">数据采集</td>
</tr>
<tr>
<td align="center">采集搜索结果数据</td>
<td align="center">数据采集</td>
</tr>
<tr>
<td align="center">采集抖音热榜数据</td>
<td align="center">数据采集</td>
</tr>
<tr>
<td align="center">批量下载收藏作品</td>
<td align="center">文件下载,数据采集</td>
</tr>
<tr>
<td align="center">批量下载收藏夹作品</td>
<td align="center">文件下载,数据采集</td>
</tr>
<tr>
<td align="center">批量下载收藏音乐作品</td>
<td align="center">文件下载,数据采集</td>
</tr>
</tbody></table>
<h2>关闭平台功能</h2>
<p>本项目支持抖音平台和 TikTok 平台的数据采集和文件下载功能,平台功能默认开启,如果不需要使用平台的任何功能,可以编辑配置文件关闭平台功能。</p>
<p>本项目内置参数更新机制,程序会周期性更新抖音与 TikTok 请求的部分参数,以保持参数的有效性(或许没有效果?),该功能无法防止参数失效,参数失效后需要重新写入 Cookie;关闭平台功能后,对应平台的参数更新功能将会禁用!</p>
<h1>配置文件</h1>
<p>配置文件:项目根目录下的 <code>./Volume/settings.json</code> 文件,可以自定义设置程序部分运行参数。</p>
<p>若无特殊需求,大部分配置参数无需修改,直接使用默认值即可。</p>
<p><b><code>cookie</code>、<code>cookie_tiktok</code> 与 <code>device_id</code>参数为必需参数,必须设置该参数才能正常使用程序</b>;其余参数可以根据实际需求进行修改!</p>
<p>如果您的计算机没有合适的程序编辑 JSON 文件,建议使用 <a href="https://www.toolhelper.cn/JSON/JSONFormat">在线工具</a> 编辑配置文件内容,修改后需要重启软件才能生效。</p>
<p>注意: 手动修改 <code>settings.json</code> 后需要重新运行程序才会生效!</p>
<h2>参数说明</h2>
<table>
<thead>
<tr>
<th align="center">参数</th>
<th align="center">类型</th>
<th align="center">说明</th>
<th align="center">默认</th>
</tr>
</thead>
<tbody>
<tr>
<td align="center"><i>mark</i></td>
<td align="center">str</td>
<td align="center"><a href="#mark"><sup>1</sup></a>账号/合集标识,用于区分账号/合集;<strong>属于 accounts_urls、mix_urls 和 owner_url 子参数</strong></td>
<td align="center">账号昵称/合集标题</td>
</tr>
<tr>
<td align="center"><i>url</i></td>
<td align="center">str</td>
<td align="center">账号主页/合集作品链接;<strong>属于 accounts_urls、mix_urls 和 owner_url 子参数</strong></td>
<td align="center">无</td>
</tr>
<tr>
<td align="center"><i>tab</i></td>
<td align="center">str</td>
<td align="center"><a href="#supplement"><sup>2</sup></a>主页标签,<code>post</code> 代表发布作品、<code>favorite</code> 代表喜欢作品;<strong>属于 accounts_urls 子参数</strong></td>
<td align="center">发布作品</td>
</tr>
<tr>
<td align="center"><i>earliest</i></td>
<td align="center">str | float | int</td>
<td align="center">作品最早发布日期,格式:<code>2023/1/1</code>、<code>整数</code>、<code>浮点数</code>;设置为数值代表基于 <code>latest</code>参数的前 XX 天,<strong>属于 accounts_urls 子参数</strong></td>
<td align="center">不限制</td>
</tr>
<tr>
<td align="center"><i>latest</i></td>
<td align="center">str | float | int</td>
<td align="center">作品最晚发布日期,格式:<code>2023/1/1</code>、<code>整数</code>、<code>浮点数</code>;设置为数值代表基于当天的前 XX 天,<strong>属于 accounts_urls 子参数</strong></td>
<td align="center">不限制</td>
</tr>
<tr>
<td align="center"><i>enable</i></td>
<td align="center">bool</td>
<td align="center">参数对象是否启用,设置为 <code>false</code> 时程序会跳过处理;<strong>属于 accounts_urls 和 mix_urls 子参数</strong></td>
<td align="center">启用</td>
</tr>
<tr>
<td align="center">accounts_urls[mark, url, tab, earliest, latest, enable]</td>
<td align="center">list[dict[str, str, str, Any, str, bool]]</td>
<td align="center"><a href="#supplement"><sup>3</sup></a>抖音平台:账号标识,账号链接,主页标签,最早发布日期,最晚发布日期,是否启用;作为 <code>批量下载账号作品</code> 模式选项,支持多账号,以字典格式包含六个参数</td>
<td align="center">无</td>
<tr>
<td align="center">mix_urls[mark, url, enable]</td>
<td align="center">list[dict[str, str, bool]]</td>
<td align="center"><a href="#supplement"><sup>3</sup></a>抖音平台:合集标识,合集链接或作品链接,是否启用;作为 <code>批量下载合集作品</code> 模式选项,支持多合集,以字典格式包含三个参数</td>
<td align="center">无</td>
</tr>
<tr>
<td align="center">owner_url[mark, url]</td>
<td align="center">dict[str, str]</td>
<td align="center"><a href="#supplement"><sup>3</sup></a>抖音平台:当前登录 Cookie 的账号标识,账号主页链接;<code>批量下载收藏作品</code> 模式下用于获取账号信息,以字典格式包含两个参数</td>
<td align="center">无</td>
</tr>
<tr>
<td align="center">accounts_urls_tiktok[mark, url, tab, earliest, latest, enable]</td>
<td align="center">list[dict[str, str, str, Any, str, bool]]</td>
<td align="center"><a href="#supplement"><sup>3</sup></a>TikTok 平台;参数规则与 <code>accounts_urls</code> 一致</td>
<td align="center">无</td>
</tr>
<tr>
<td align="center">mix_urls_tiktok[mark, url, enable]</td>
<td align="center">list[dict[str, str, bool]]</td>
<td align="center"><a href="#supplement"><sup>3</sup></a>TikTok 平台;参数规则与 <code>mix_urls</code> 一致</td>
<td align="center">无</td>
</tr>
<tr>
<td align="center">owner_url_tiktok[mark, url](未生效)</td>
<td align="center">dict[str, str]</td>
<td align="center"><a href="#supplement"><sup>3</sup></a>TikTok 平台;参数规则与 <code>owner_url</code> 一致</td>
<td align="center">无</td>
</tr>
<tr>
<td align="center">root</td>
<td align="center">str</td>
<td align="center">作品文件和数据记录保存路径;建议使用绝对路径</td>
<td align="center">项目根路径/Volume</td>
</tr>
<tr>
<td align="center">folder_name</td>
<td align="center">str</td>
<td align="center">批量下载链接作品时,保存文件夹的名称</td>
<td align="center">Download</td>
</tr>
<tr>
<td align="center">name_format</td>
<td align="center">str</td>
<td align="center">文件保存时的命名规则,值之间使用空格分隔,支持:<code>id</code>:作品 ID;<code>desc</code>:作品描述;<code>create_time</code>:发布时间;<code>nickname</code>:账号昵称;<code>mark</code>:账号标识;<code>uid</code>:账号 ID;<code>type</code>:作品类型</td>
<td align="center">发布时间-作品类型-账号昵称-描述</td>
</tr>
<tr>
<td align="center">desc_length</td>
<td align="center">int</td>
<td align="center">作品文件名中描述字段的最大字符数;超过限制的描述字段将折叠处理</td>
<td align="center">64</td>
</tr>
<tr>
<td align="center">name_length</td>
<td align="center">int</td>
<td align="center">作品文件名称的最大字符数;超过限制的文件名称将折叠处理</td>
<td align="center">128</td>
</tr>
<tr>
<td align="center">date_format</td>
<td align="center">str</td>
<td align="center">日期时间格式;<a href="https://docs.python.org/zh-cn/3/library/time.html?highlight=strftime#time.strftime">点击查看设置规则</a></td>
<td align="center">年-月-日 时:分:秒</td>
</tr>
<tr>
<td align="center">split</td>
<td align="center">str</td>
<td align="center">文件命名的分隔符</td>
<td align="center">-</td>
</tr>
<tr>
<td align="center">folder_mode</td>
<td align="center">bool</td>
<td align="center">是否将每个作品的文件储存至单独的文件夹,文件夹名称格式与 <code>name_format</code> 参数一致</td>
<td align="center">false</td>
</tr>
<tr>
<td align="center">music</td>
<td align="center">bool</td>
<td align="center">是否下载作品音乐</td>
<td align="center">false</td>
</tr>
<tr>
<td align="center">truncate</td>
<td align="center">int</td>
<td align="center">文件下载进度条中描述字符串的最大长度,该参数用于调整显示效果</td>
<td align="center">64</td>
</tr>
<tr>
<td align="center">storage_format</td>
<td align="center">str</td>
<td align="center"><a href="#supplement"><sup>3</sup></a>采集数据持久化储存格式,支持:<code>csv</code>、<code>xlsx</code>、<code>sql</code>(SQLite)</td>
<td align="center">不保存</td>
</tr>
<tr>
<td align="center">cookie</td>
<td align="center">dict | str</td>
<td align="center"><a href="#supplement"><sup>4</sup></a>抖音网页版 Cookie, 必需参数; 建议通过程序写入配置文件,亦可手动编辑</td>
<td align="center">无</td>
</tr>
<tr>
<td align="center">cookie_tiktok</td>
<td align="center">dict | str</td>
<td align="center"><a href="#supplement"><sup>4</sup></a>TikTok 网页版 Cookie, 必需参数; 建议通过程序写入配置文件,亦可手动编辑</td>
<td align="center">无</td>
</tr>
<tr>
<td align="center">dynamic_cover</td>
<td align="center">bool</td>
<td align="center">是否下载视频作品动态封面图</td>
<td align="center">false</td>
</tr>
<tr>
<td align="center">static_cover</td>
<td align="center">bool</td>
<td align="center">是否下载视频作品静态封面图</td>
<td align="center">false</td>
</tr>
<tr>
<td align="center">proxy</td>
<td align="center">str</td>
<td align="center">抖音请求代理地址</td>
<td align="center">不使用代理</td>
</tr>
<tr>
<td align="center">proxy_tiktok</td>
<td align="center">str</td>
<td align="center">TikTok 请求代理地址</td>
<td align="center">不使用代理</td>
</tr>
<tr>
<td align="center"><a href="#twc">twc_tiktok</a></td>
<td align="center">str</td>
<td align="center">TikTok Cookie 的 ttwid 值,一般情况下无需设置</td>
<td align="center">无</td>
</tr>
<tr>
<td align="center">download</td>
<td align="center">bool</td>
<td align="center">是否开启项目的下载功能,如果关闭,程序将不会下载任何文件</td>
<td align="center">true</td>
</tr>
<tr>
<td align="center">max_size</td>
<td align="center">int</td>
<td align="center">作品文件大小限制,单位字节,超出大小限制的作品文件将会跳过下载</td>
<td align="center">无限制</td>
</tr>
<tr>
<td align="center">chunk</td>
<td align="center">int</td>
<td align="center">每次从服务器接收的数据块大小,单位字节</td>
<td align="center">2097152(2 MB)</td>
</tr>
<tr>
<td align="center">timeout</td>
<td align="center">int</td>
<td align="center">请求数据的超时限制,单位秒</td>
<td align="center">10</td>
</tr>
<tr>
<td align="center">max_retry</td>
<td align="center">int</td>
<td align="center">发送请求获取数据发生异常时重试的最大次数,设置为 <code>0</code> 代表关闭重试</td>
<td align="center">10</td>
</tr>
<tr>
<td align="center">max_pages</td>
<td align="center">int</td>
<td align="center">批量下载账号喜欢作品、收藏作品或者采集作品评论数据时,请求数据的最大次数(不包括异常重试)</td>
<td align="center">不限制</td>
</tr>
<tr>
<td align="center">run_command</td>
<td align="center">str</td>
<td align="center">设置程序启动执行的默认命令,相当于模拟用户输入序号或内容(多个序号或内容之间使用空格分隔)</td>
<td align="center">无</td>
</tr>
<tr>
<td align="center">ffmpeg</td>
<td align="center">str</td>
<td align="center"><a href="#supplement"><sup>3</sup></a><code>ffmpeg.exe</code> 路径,下载直播时使用,如果系统环境存在 <code>ffmpeg</code> 或者不想使用 <code>ffmpeg</code>,无需设置该参数</td>
<td align="center">无</td>
</tr>
<tr>
<td align="center">live_qualities</td>
<td align="center">str</td>
<td align="center"><a href="#supplement"><sup>3</sup></a>下载直播时的默认清晰度,支持设置为清晰度或者序号;当设置了该参数时,获取直播拉流地址将会直接下载指定清晰度的直播文件,不再提示输入清晰度;参数示例:<code>FULL_HD1</code>、<code>HD1</code>、<code>1</code>、<code>2</code> 等</td>
<td align="center">无</td>
</tr>
<tr>
<td align="center">douyin_platform</td>
<td align="center">bool</td>
<td align="center"><a href="#supplement"><sup>5</sup></a>是否启用抖音平台功能</td>
<td align="center">true</td>
</tr>
<tr>
<td align="center">tiktok_platform</td>
<td align="center">bool</td>
<td align="center"><a href="#supplement"><sup>5</sup></a>是否启用 TikTok 平台功能</td>
<td align="center">true</td>
</tr>
<tr>
<td align="center">browser_info</td>
<td align="center">dict</td>
<td align="center">抖音平台浏览器信息,一般情况下无需修改</td>
<td align="center">内置参数</td>
</tr>
<tr>
<td align="center">browser_info_tiktok</td>
<td align="center">dict</td>
<td align="center">TikTok 平台浏览器信息,一般情况下仅需修改 <code>device_id</code> 参数,获取方式查阅 <a href="https://github.com/JoeanAmier/TikTokDownloader/blob/master/docs/Cookie%E8%8E%B7%E5%8F%96%E6%95%99%E7%A8%8B.md">Cookie 获取教程</a></td>
<td align="center">内置参数</td>
</tr>
</tbody>
</table>
<div id="supplement">
<p><strong>补充说明:</strong></p>
<ol>
<li><a href="#mark">详见标识参数说明</a></li>
<li>设置为 <code>favorite</code> 时,需要确保账号喜欢作品公开可见,或者配置对应账号的登录 Cookie</li>
<li>该参数仅在部分模式和功能中生效,如果不需要使用相应的模式和功能,无需设置该参数</li>
<li>必须设置平台的 Cookie 才能使用该平台的数据采集和文件下载功能</li>
<li>如果不需要使用该平台的任何功能,可以将该参数设置为 <code>false</code></li>
</ol>
</div>
<h2>配置示例</h2>

```json
{
  "accounts_urls": [
    {
      "mark": "账号A",
      "url": "https://www.douyin.com/user/aaa",
      "tab": "post",
      "earliest": "2024/3/1",
      "latest": "2024/7/1",
      "enable": true
    },
    {
      "mark": "账号B",
      "url": "https://v.douyin.com/bbb",
      "tab": "favorite",
      "earliest": 30,
      "latest": "",
      "enable": false
    }
  ],
  "accounts_urls_tiktok": "参数规则与 accounts_urls 一致",
  "mix_urls": [
    {
      "mark": "",
      "url": "https://v.douyin.com/ccc",
      "enable": true
    },
    {
      "mark": "合集B",
      "url": "https://www.douyin.com/video/123",
      "enable": false
    }
  ],
  "mix_urls_tiktok": "参数规则与 mix_urls 一致",
  "owner_url": {
    "mark": "已登录 Cookie 的账号标识,可以设置为空字符串",
    "url": "已登录 Cookie 的账号主页链接"
  },
  "owner_url_tiktok": "参数规则与 owner_url 一致",
  "root": "C:\\DouK-Downloader",
  "folder_name": "SOLO",
  "name_format": "create_time uid id",
  "desc_length": 64,
  "name_length": 128,
  "date_format": "%Y-%m-%d",
  "split": " ",
  "folder_mode": false,
  "music": false,
  "truncate": 32,
  "storage_format": "xlsx",
  "cookie": {
    "key-1": "value-1",
    "key-2": "value-2",
    "key-3": "value-3"
  },
  "cookie_tiktok": "参数规则与 cookie 一致",
  "dynamic_cover": false,
  "static_cover": false,
  "proxy": "http://127.0.0.1:9999",
  "proxy_tiktok": "参数规则与 proxy 一致",
  "twc_tiktok": "",
  "download": true,
  "max_size": 104857600,
  "chunk": 10485760,
  "timeout": 5,
  "max_retry": 10,
  "max_pages": 2,
  "run_command": "6 2 1",
  "ffmpeg": "C:\\DouK-Downloader\\ffmpeg.exe",
  "live_qualities": "1",
  "douyin_platform": true,
  "tiktok_platform": true,
  "browser_info": {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36",
    "pc_libra_divert": "Windows",
    "browser_language": "zh-SG",
    "browser_platform": "Win32",
    "browser_name": "Chrome",
    "browser_version": "139.0.0.0",
    "engine_name": "Blink",
    "engine_version": "139.0.0.0",
    "os_name": "Windows",
    "os_version": "10",
    "webid": ""
  },
  "browser_info_tiktok": {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36",
    "app_language": "zh-Hans",
    "browser_language": "zh-SG",
    "browser_name": "Mozilla",
    "browser_platform": "Win32",
    "browser_version": "5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36",
    "language": "zh-Hans",
    "os": "windows",
    "priority_region": "CN",
    "region": "US",
    "tz_name": "Asia/Shanghai",
    "webcast_language": "zh-Hans",
    "device_id": "0123456789"
  }
}
```

<h2>参数详解</h2>
<h3>下载喜欢作品</h3>

```json
{
  "accounts_urls": [
    {
      "mark": "",
      "url": "https://www.douyin.com/user/aaa",
      "tab": "favorite",
      "earliest": "",
      "latest": "",
      "enable": true
    },
    {
      "mark": "",
      "url": "https://v.douyin.com/bbb",
      "tab": "post",
      "earliest": "",
      "latest": "",
      "enable": true
    },
    {
      "mark": "",
      "url": "https://www.douyin.com/user/ccc",
      "tab": "favorite",
      "earliest": "",
      "latest": "",
      "enable": false
    }
  ]
}
```

<p>将待下载的账号信息写入配置文件,每个账号对应一个对象/字典,<code>tab</code> 参数设置为 <code>favorite</code> 代表批量下载喜欢作品,支持多账号;<code>accounts_urls_tiktok</code>参数规则一致。</p>
<p>下载账号喜欢作品需要确保账号喜欢作品公开可见,或者配置对应账号的登录 Cookie!</p>
<p><b>下载账号喜欢作品需要使用已登录的 Cookie,否则可能无法获取正确的账号信息!</b></p>
<h3>发布日期限制</h3>

```json
{
  "accounts_urls": [
    {
      "mark": "账号A",
      "url": "https://v.douyin.com/aaa",
      "tab": "post",
      "earliest": "2023/12/1",
      "latest": "",
      "enable": true
    },
    {
      "mark": "",
      "url": "https://v.douyin.com/bbb",
      "tab": "post",
      "earliest": 30,
      "latest": "2024/12/1",
      "enable": true
    }
  ]
}
```

<p>如果已经采集某账号的全部发布作品,建议设置 <code>earliest</code> 和 <code>latest</code> 参数以减少后续采集请求次数,提高程序运行效率;<code>accounts_urls_tiktok</code>参数规则一致。</p>
<p>示例:将 <code>earliest</code> 参数设置为 <code>2023/12/1</code>,程序获取账号发布作品数据时,不会获取早于 <code>2023/12/1</code> 的作品数据。</p>
<p>示例:将 <code>earliest</code> 参数设置为 <code>30</code>,<code>latest</code> 参数设置为 <code>2024/12/1</code>,程序获取账号发布作品数据时,仅获取 2024 年 12 月 1 日当天及之前 30 天内发布的作品数据。</p>
<p>示例:将 <code>earliest</code> 参数设置为 <code>15</code>,<code>latest</code> 参数设置为 <code>5</code>,程序获取账号发布作品数据时,仅获取前 5 天 ~ 前 20 天之间发布的作品数据。</p>
<h3>文件储存路径</h3>

```json
{
  "root": "C:\\DouK-Downloader",
  "folder_name": "SOLO"
}
```

<p>程序会将账号作品和合集作品的文件 和 记录的数据储存至 <code>C:\DouK-Downloader</code> 文件夹内,链接作品的文件会储存至 <code>C:\DouK-Downloader\SOLO</code> 文件夹内。</p>
<h3>文件名称格式</h3>

```json
{
  "name_format": "create_time uid id",
  "split": " @ "
}
```

<p>作品文件名称格式为: <code>发布时间 @ 作者UID @ 作品ID</code></p>
<ul>
<li>如果作品没有描述,文件名称的描述内容将替换为作品 ID。</li>
<li>批量下载链接作品时,如果在 <code>name_format</code> 参数中设置了 <code>mark</code> 字段,程序会自动替换为 <code>nickname</code> 字段。</li>
</ul>
<h3>日期时间格式</h3>

```json
{
  "date_format": "%Y-%m-%d"
}
```

<p>发布时间格式为:XXXX年-XX月-XX日,详细设置规则可以 <a href="https://docs.python.org/zh-cn/3/library/time.html?highlight=strftime#time.strftime">查看文档</a></p>
<h3>数据储存格式</h3>

```json
{
  "storage_format": "xlsx"
}
```

<p>使用 <code>XLSX</code> 格式储存程序采集数据。</p>
<h3>文件大小限制</h3>

```json
{
  "max_size": 104857600
}
```

<p>作品文件大小限制为 104857600 字节(100 MB),超过该大小的作品文件会自动跳过下载;直播文件不受限制。</p>
<h3>文件分块下载</h3>

```json
{
  "chunk": 10485760
}
```

<p>下载文件时每次从服务器接收 10485760 字节 (10 MB)的数据块。</p>
<ul>
<li>影响下载速度:较大的 chunk 会增加每次下载的数据量,从而提高下载速度。相反,较小的 chunk 会降低每次下载的数据量,可能导致下载速度稍慢。</li>
<li>影响内存占用:较大的 chunk 会一次性加载更多的数据到内存中,可能导致内存占用增加。相反,较小的 chunk 会减少每次加载的数据量,从而降低内存占用。</li>
</ul>
<h3>请求次数限制</h3>

```json
{
  "max_pages": 2
}
```

<p>下载账号喜欢作品、收藏作品以及采集作品评论数据时,仅获取前 <code>2</code> 页数据;用于解决下载账号喜欢作品、收藏作品需要获取全部数据的问题,以及作品评论数据数量过多的采集问题。</p>
<p>不影响下载账号发布作品,如需控制账号发布作品数据获取次数,请使用 <code>earliest</code> 和 <code>latest</code> 参数实现。</p>
<h3>默认执行命令</h3>

```json
{
  "run_command": "6 1 1 Q"
}
```

<p>上述命令表示运行程序自动依次执行 <code>终端交互模式</code> -> <code>批量下载账号作品(抖音)</code> -> <code>使用 accounts_urls 参数的账号链接(推荐)</code> -> <code>退出程序</code></p>
<p>该参数可以实现设置默认启动模式、运行功能后自动退出、自动读取浏览器 Cookie 等高级自动化功能!</p>
<ul>其他示例:
<li><code>6 2</code>:代表依次执行 <code>终端交互模式</code> -> <code>批量下载账号作品(抖音)</code></li>
<li><code>8</code>:代表执行<code>Web API 模式</code></li>
<li><code>2 7</code>:代表依次执行<code>从浏览器读取 Cookie (抖音)</code> -> <code>Edge</code></li>
</ul>
<h3>程序代理设置</h3>

```json
{
  "proxy": "http://127.0.0.1:9999"
}
```

<p>程序获取网络数据时使用 <code>http://127.0.0.1:9999</code> 作为代理;程序会自动验证代理是否可用,如果代理不可用,则 <code>proxy</code> 参数不生效。</p>
<p>如果您的电脑使用了代理工具且未修改默认端口,可以尝试以下设置:</p>
<ul>
<li>Clash: <code>http://127.0.0.1:7890</code></li>
<li>v2rayN: <code>http://127.0.0.1:10809</code></li>
</ul>
<h1>高级配置</h1>
<p>如果想要进一步修改程序功能,可以编辑 <code>src/custom</code> 文件夹内容(不适用于可执行文件),按照注释指引和实际需求进行自定义修改。</p>
<b>部分可自定义设置的功能:</b>
<ul>
<li>设置作品文件下载的最大线程数量</li>
<li>设置非法字符替换规则</li>
<li>设置服务器模式主机及端口</li>
<li>设置平台参数更新间隔</li>
<li>设置彩色交互提示颜色</li>
<li>设置请求数据延时间隔</li>
<li>设置自定义作品筛选规则</li>
<li>设置分批获取数据策略</li>
<li>设置服务器模式参数验证</li>
</ul>
<h1>功能介绍</h1>
<h2>从剪贴板读取 Cookie</h2>
<p>参考 <a href="https://github.com/JoeanAmier/TikTokDownloader/blob/master/docs/Cookie%E8%8E%B7%E5%8F%96%E6%95%99%E7%A8%8B.md">Cookie 提取教程</a>,手动从浏览器复制所需 Cookie 内容至剪贴板,再按照程序提示操作;程序会自动读取剪贴板的内容并将有效的 Cookie 写入配置文件。</p>
<p>成功写入配置文件后,程序会提示当前 Cookie 登录状态!</p>
<h2>从浏览器读取 Cookie</h2>
<p>自动读取本地浏览器的 Cookie 数据,并提取所需 Cookie 写入配置文件。</p>
<p>成功写入配置文件后,程序会提示当前 Cookie 登录状态!</p>
<p>Windows 系统需要以管理员身份运行程序才能读取 Chromium、Chrome、Edge 浏览器 Cookie!</p>
<p><strong>兼容性提醒:此功能依赖的第三方模块已长期未更新,可能无法正常支持最新浏览器版本。若功能出现异常,请尝试手动获取 Cookie!</strong></p>
<h2><del>扫码登录获取 Cookie</del></h2>
<p><del>程序自动获取抖音登录二维码,随后会在终端输出二维码,并使用系统默认图片浏览器打开二维码图片,使用者通过抖音 APP 扫码并登录账号,操作后关闭二维码图片窗口,程序会自动检查登录结果并将登录后的 Cookie 写入配置文件。</del></p>
<p><b>注意:</b>扫码登录可能会导致抖音账号被风控,该功能仅限学习研究,未来可能禁用或移除该功能!</p>
<h2>终端交互模式</h2>
<p>功能最全面的模式,支持全部功能。</p>
<h3>批量下载账号作品(抖音)</h3>
<ol>
<li>使用 <code>settings.json</code> 的 <code>accounts_urls</code> 参数中的账号链接。</li>
<li>手动输入待采集的账号链接;此选项仅支持批量下载账号发布页作品,暂不支持参数设置。</li>
<li>输入文本文档路径,读取文件包含的账号链接;此选项仅支持批量下载账号发布页作品,暂不支持参数设置。</li>
</ol>
<p>支持链接格式:</p>
<ul>
<li><code>https://v.douyin.com/分享码/</code></li>
<li><code>https://www.douyin.com/user/账号ID</code></li>
<li><code>https://www.douyin.com/user/账号ID?modal_id=作品ID</code></li>
</ul>
<p><del>如果需要大批量采集账号作品,建议启用 <code>src/custom/function.py</code> 文件的 <code>suspend</code> 方法。</del>(默认启用)</p>
<p><b>下载账号喜欢作品时需要使用已登录的 Cookie,否则程序可能无法正常获取账号消息!</b></p>
<p>如果当前账号昵称或账号标识不是有效的文件夹名称时,程序会自动替换为账号 ID。</p>
<p>每个账号的作品会下载至 <code>root</code> 参数路径下的账号文件夹,账号文件夹格式为 <code>UID123456789_mark_类型</code> 或者 <code>UID123456789_账号昵称_类型</code></p>
<h3>批量下载链接作品(抖音)</h3>
<ol>
<li>手动输入待采集的作品链接。</li>
<li>输入文本文档路径,读取文件包含的作品链接。</li>
</ol>
<p>支持链接格式:</p>
<ul>
<li><code>https://v.douyin.com/分享码/</code></li>
<li><code>https://www.douyin.com/note/作品ID</code></li>
<li><code>https://www.douyin.com/video/作品ID</code></li>
<li><code>https://www.douyin.com/discover?modal_id=作品ID</code></li>
<li><code>https://www.douyin.com/user/账号ID?modal_id=作品ID</code></li>
<li><code>https://www.douyin.com/search/关键词?modal_id=作品ID</code></li>
<li><code>https://www.douyin.com/channel/分区ID?modal_id=作品ID</code></li>
</ul>
<p>作品会下载至 <code>root</code> 参数和 <code>folder_name</code> 参数拼接成的文件夹。</p>
<h3>获取直播拉流地址(抖音)</h3>
<p>输入直播链接,不支持已结束的直播。</p>
<p>支持链接格式:</p>
<ul>
<li><code>https://live.douyin.com/直播ID</code></li>
<li><code>https://v.douyin.com/分享码/</code></li>
<li><code>https://www.douyin.com/follow?webRid=直播ID</code></li>
</ul>
<p>下载说明:</p>
<ul>
<li>程序会询问用户是否下载直播视频,支持同时下载多个直播视频。</li>
<li>程序调用 <code>ffmpeg</code> 下载直播时,关闭 DouK-Downloader 不会影响直播下载。</li>
<li><del>程序调用内置下载器下载直播时,需要保持 DouK-Downloader 运行直到直播结束。</del></li>
<li>程序询问是否下载直播时,输入直播清晰度或者对应序号即可下载,例如:下载最高清晰度输入 <code>FULL_HD1</code> 或者 <code>1</code> 均可。</li>
<li><del>程序调用内置下载器下载的直播文件,视频时长会显示为直播总时长,实际视频内容从下载时间开始,靠后部分的片段无法播放。</del></li>
<li>直播视频会下载至 <code>root</code> 参数路径下的 <code>Live</code> 文件夹。</li>
<li>按下 <code>Ctrl + C</code> 终止程序或 <code>ffmpeg</code> 并不会导致已下载文件丢失或损坏,但无法继续下载。</li>
</ul>
<h3>采集作品评论数据(抖音)</h3>
<p><strong>评论回复采集功能暂不开放!</strong></p>
<ol>
<li>手动输入待采集的作品链接。</li>
<li>输入文本文档路径,读取文件包含的作品链接。</li>
</ol>
<p>支持链接格式:</p>
<ul>
<li><code>https://v.douyin.com/分享码/</code></li>
<li><code>https://www.douyin.com/note/作品ID</code></li>
<li><code>https://www.douyin.com/video/作品ID</code></li>
<li><code>https://www.douyin.com/discover?modal_id=作品ID</code></li>
<li><code>https://www.douyin.com/user/账号ID?modal_id=作品ID</code></li>
<li><code>https://www.douyin.com/search/关键词?modal_id=作品ID</code></li>
<li><code>https://www.douyin.com/channel/分区ID?modal_id=作品ID</code></li>
</ul>
<p>支持采集<del>评论回复</del>、评论表情、评论图片;必须设置 <code>storage_format</code> 参数才能正常使用。</p>
<p>储存名称格式:<code>作品123456789_评论数据</code></p>
<h3>批量下载合集作品(抖音)</h3>
<ol>
<li>使用 <code>settings.json</code> 的 <code>mix_urls</code> 参数中的合集链接或作品链接。</li>
<li>获取当前登录 Cookie 的收藏合集信息,并由使用者选择需要下载的合集;该选项暂不支持设置合集标识。</li>
<li>输入合集链接,或者属于合集的任意一个作品链接;该选项暂不支持设置合集标识。</li>
<li>输入文本文档路径,读取文件包含的作品链接或合集链接;该选项暂不支持设置合集标识。</li>
</ol>
<p>支持链接格式:</p>
<ul>
<li><code>https://v.douyin.com/分享码/</code></li>
<li><code>https://www.douyin.com/note/作品ID</code></li>
<li><code>https://www.douyin.com/video/作品ID</code></li>
<li><code>https://www.douyin.com/discover?modal_id=作品ID</code></li>
<li><code>https://www.douyin.com/user/账号ID?modal_id=作品ID</code></li>
<li><code>https://www.douyin.com/search/关键词?modal_id=作品ID</code></li>
<li><code>https://www.douyin.com/collection/合集ID</code></li>
<li><code>https://www.douyin.com/channel/分区ID?modal_id=作品ID</code></li>
</ul>
<p><del>如果需要大批量采集合集作品,建议启用 <code>src/custom/function.py</code> 文件的 <code>suspend</code> 方法。</del>(默认启用)</p>
<p>如果当前合集标题或合集标识不是有效的文件夹名称时,程序会自动替换为合集 ID。</p>
<p>每个合集的作品会下载至 <code>root</code> 参数路径下的合集文件夹,合集文件夹格式为 <code>MIX123456789_mark_合集作品</code> 或者 <code>MIX123456789_合集标题_合集作品</code></p>
<h3>采集账号详细数据(抖音)</h3>
<ol>
<li>使用 <code>settings.json</code> 的 <code>accounts_urls</code> 参数中的账号链接。</li>
<li>手动输入待采集的账号链接。</li>
<li>输入文本文档路径,读取文件包含的账号链接。</li>
</ol>
<p>支持链接格式:</p>
<ul>
<li><code>https://v.douyin.com/分享码/</code></li>
<li><code>https://www.douyin.com/user/账号ID</code></li>
<li><code>https://www.douyin.com/user/账号ID?modal_id=作品ID</code></li>
</ul>
<p>重复获取相同账号数据时会储存为新的数据行,不会覆盖原有数据;必须设置 <code>storage_format</code> 参数才能正常使用。</p>
<h3>采集搜索结果数据(抖音)</h3>
<h4>搜索条件规则</h4>
<ul>
<li>
<strong>综合搜索参数顺序:</strong><code>关键词</code>;<code>总页数</code>;<code>排序依据</code>;<code>发布时间</code>;<code>视频时长</code>;<code>搜索范围</code>;<code>内容格式</code>
</li>
<li>
<strong>视频搜索参数顺序:</strong><code>关键词</code>;<code>总页数</code>;<code>排序依据</code>;<code>发布时间</code>;<code>视频时长</code>;<code>搜索范围</code>
</li>
<li>
<strong>用户搜索参数顺序:</strong><code>关键词</code>;<code>总页数</code>;<code>粉丝数量</code>;<code>用户类型</code>
</li>
<li>
<strong>直播搜索参数顺序:</strong><code>关键词</code>;<code>总页数</code>
</li>
</ul>
<h4>参数含义</h4>
<ul>
<li>排序依据:<code>0</code>:综合排序;<code>1</code>:最多点赞;<code>2</code>:最新发布</li>
<li>发布时间:<code>0</code>:不限;<code>1</code>:一天内;<code>7</code>:一周内;<code>180</code>:半年内</li>
<li>视频时长:<code>0</code>:不限;<code>1</code>:一分钟以内;<code>2</code>:一到五分钟;<code>3</code>:五分钟以上</li>
<li>搜索范围:<code>0</code>:不限;<code>1</code>:最近看过;<code>2</code>:还未看过;<code>3</code>:关注的人</li>
<li>内容形式:<code>0</code>:不限;<code>1</code>:视频;<code>2</code>:图文</li>
<li>粉丝数量:<code>0</code>:不限;<code>1</code>:1000以下;<code>2</code>:1000-1W;<code>3</code>:1W-10W;<code>4</code>:10W-100W;<code>5</code>:100W以上</li>
<li>用户类型:<code>0</code>:不限;<code>1</code>:普通用户;<code>2</code>:企业认证;<code>3</code>:个人认证</li>
</ul>
<p><strong>参数之间使用两个空格分隔;除了搜索关键词以外的参数均只支持输入数值;未输入的参数均视为 <code>0</code></strong></p>
<p>程序采集的搜索结果数据会储存至文件;暂不支持直接下载搜索结果作品;必须设置 <code>storage_format</code> 参数才能正常使用。</p>
<h4>参数输入示例</h4>
<h5>综合搜索/视频搜索</h5>
<p><strong>输入:</strong><code>猫咪</code></p>
<table>
<tr>
<th align="center" rowspan="2">含义</th>
<th align="center">关键词</th>
<th align="center">总页数</th>
<th align="center">排序依据</th>
<th align="center">发布时间</th>
<th align="center">视频时长</th>
<th align="center">搜索范围</th>
<th align="center">内容形式</th>
</tr>
<tr>
<td align="center">猫咪</td>
<td align="center">1</td>
<td align="center">不限</td>
<td align="center">不限</td>
<td align="center">不限</td>
<td align="center">不限</td>
<td align="center">不限</td>
</tr>
</table>
<hr>
<p><strong>输入:</strong><code>猫咪&nbsp;&nbsp;2&nbsp;&nbsp;2&nbsp;&nbsp;7&nbsp;&nbsp;0&nbsp;&nbsp;1</code></p>
<table>
<tr>
<th align="center" rowspan="2">含义</th>
<th align="center">关键词</th>
<th align="center">总页数</th>
<th align="center">排序依据</th>
<th align="center">发布时间</th>
<th align="center">视频时长</th>
<th align="center">搜索范围</th>
<th align="center">内容形式</th>
</tr>
<tr>
<td align="center">猫咪</td>
<td align="center">2</td>
<td align="center">最新发布</td>
<td align="center">一周内</td>
<td align="center">不限</td>
<td align="center">最近看过</td>
<td align="center">不限</td>
</tr>
</table>
<hr>
<p><strong>输入:</strong><code>猫咪&nbsp;&nbsp;10&nbsp;&nbsp;0&nbsp;&nbsp;0&nbsp;&nbsp;0&nbsp;&nbsp;3</code></p>
<table>
<tr>
<th align="center" rowspan="2">含义</th>
<th align="center">关键词</th>
<th align="center">总页数</th>
<th align="center">排序依据</th>
<th align="center">发布时间</th>
<th align="center">视频时长</th>
<th align="center">搜索范围</th>
<th align="center">内容形式</th>
</tr>
<tr>
<td align="center">猫咪</td>
<td align="center">10</td>
<td align="center">不限</td>
<td align="center">不限</td>
<td align="center">不限</td>
<td align="center">关注的人</td>
<td align="center">不限</td>
</tr>
</table>
<hr>
<p><strong>输入:</strong><code>猫咪&nbsp;白&nbsp;&nbsp;5&nbsp;&nbsp;0&nbsp;&nbsp;180</code></p>
<table>
<tr>
<th align="center" rowspan="2">含义</th>
<th align="center">关键词</th>
<th align="center">总页数</th>
<th align="center">排序依据</th>
<th align="center">发布时间</th>
<th align="center">视频时长</th>
<th align="center">搜索范围</th>
<th align="center">内容形式</th>
</tr>
<tr>
<td align="center">猫咪 白</td>
<td align="center">5</td>
<td align="center">不限</td>
<td align="center">半年内</td>
<td align="center">不限</td>
<td align="center">不限</td>
<td align="center">不限</td>
</tr>
</table>
<h5>用户搜索</h5>
<p><strong>输入:</strong><code>小姐姐&nbsp;&nbsp;10&nbsp;&nbsp;0&nbsp;&nbsp;0</code></p>
<table>
<tr>
<th align="center" rowspan="2">含义</th>
<th align="center">关键词</th>
<th align="center">总页数</th>
<th align="center">粉丝数量</th>
<th align="center">用户类型</th>
</tr>
<tr>
<td align="center">小姐姐</td>
<td align="center">10</td>
<td align="center">不限</td>
<td align="center">不限</td>
</tr>
</table>
<hr>
<p><strong>输入:</strong><code>小姐姐&nbsp;&nbsp;5&nbsp;&nbsp;4&nbsp;&nbsp;3</code></p>
<table>
<tr>
<th align="center" rowspan="2">含义</th>
<th align="center">关键词</th>
<th align="center">总页数</th>
<th align="center">粉丝数量</th>
<th align="center">用户类型</th>
</tr>
<tr>
<td align="center">小姐姐</td>
<td align="center">5</td>
<td align="center">10W-100W</td>
<td align="center">个人认证</td>
</tr>
</table>
<h5>直播搜索</h5>
<p><strong>输入:</strong><code>跳舞&nbsp;&nbsp;10</code></p>
<table>
<tr>
<th align="center" rowspan="2">含义</th>
<th align="center">关键词</th>
<th align="center">总页数</th>
</tr>
<tr>
<td align="center">跳舞</td>
<td align="center">10</td>
</tr>
</table>
<h3>采集抖音热榜数据(抖音)</h3>
<p>无需输入任何内容;采集 <code>抖音热榜</code>、<code>娱乐榜</code>、<code>社会榜</code>、<code>挑战榜</code> 数据并储存至文件;必须设置 <code>storage_format</code> 参数才能正常使用。</p>
<p>储存名称格式:<code>热榜数据_采集时间_热榜名称</code></p>
<h3>批量下载话题作品(抖音)</h3>
<p>暂不支持!</p>
<h3>批量下载收藏作品(抖音)</h3>
<p>无需输入任何内容;需要在配置文件写入已登录的 Cookie,并在 <code>owner_url</code> 参数填入对应的账号主页链接和账号标识(可选参数);目前仅支持采集当前 Cookie 对应账号的收藏作品。</p>
<p>文件夹格式为 <code>UID123456789_mark_收藏作品</code> 或者 <code>UID123456789_账号昵称_收藏作品</code></p>
<h3>批量下载收藏夹作品(抖音)</h3>
<p>无需输入任何内容;需要在配置文件写入已登录的 Cookie,程序会自动获取当前 Cookie 账号的收藏夹数据并展示,根据程序提示输入收藏夹序号下载对应收藏夹作品文件,输入 <code>ALL</code> 下载全部收藏夹作品。</p>
<p>文件夹格式为 <code>CID123456789_收藏夹名称_收藏作品</code></p>
<h3>批量下载账号作品(TikTok)</h3>
<ol>
<li>使用 <code>settings.json</code> 的 <code>accounts_urls_tiktok</code> 参数中的账号链接。</li>
<li>手动输入待采集的账号链接;此选项仅支持批量下载账号发布页作品,暂不支持参数设置。</li>
<li>输入文本文档路径,读取文件包含的账号链接;此选项仅支持批量下载账号发布页作品,暂不支持参数设置。</li>
</ol>
<p>支持链接格式:</p>
<ul>
<li><code>https://www.tiktok.com/@TikTok号</code></li>
<li><code>https://www.tiktok.com/@TikTok号/video/作品ID</code></li>
</ul>
<p><del>如果需要大批量采集账号作品,建议启用 <code>src/custom/function.py</code> 文件的 <code>suspend</code> 方法。</del>(默认启用)</p>
<p>如果当前账号昵称或账号标识不是有效的文件夹名称时,程序会自动替换为账号 ID。</p>
<p>每个账号的作品会下载至 <code>root</code> 参数路径下的账号文件夹,账号文件夹格式为 <code>UID123456789_mark_类型</code> 或者 <code>UID123456789_账号昵称_类型</code></p>
<h3>批量下载链接作品(TikTok)</h3>
<ol>
<li>手动输入待采集的作品链接。</li>
<li>输入文本文档路径,读取文件包含的作品链接。</li>
</ol>
<p>支持链接格式:</p>
<ul>
<li><code>https://vm.tiktok.com/分享码/</code></li>
<li><code>https://www.tiktok.com/@TikTok号/video/作品ID</code></li>
</ul>
<p>作品会下载至 <code>root</code> 参数和 <code>folder_name</code> 参数拼接成的文件夹。</p>
<h3>批量下载合集作品(TikTok)</h3>
<ol>
<li>使用 <code>settings.json</code> 的 <code>mix_urls_tiktok</code> 参数中的合集链接。</li>
<li>输入合集链接;该选项暂不支持设置合集标识。</li>
<li>输入文本文档路径,读取文件包含的合集链接;该选项暂不支持设置合集标识。</li>
</ol>
<p>支持链接格式:</p>
<ul>
<li><code>https://vt.tiktok.com/分享码/</code></li>
<li><code>https://www.tiktok.com/@TikTok号/playlist/合辑信息</code></li>
<li><code>https://www.tiktok.com/@TikTok号/collection/合辑信息</code></li>
</ul>
<p><del>如果需要大批量采集合集作品,建议启用 <code>src/custom/function.py</code> 文件的 <code>suspend</code> 方法。</del>(默认启用)</p>
<p>如果当前合集标题或合集标识不是有效的文件夹名称时,程序会自动替换为合集 ID。</p>
<p>每个合集的作品会下载至 <code>root</code> 参数路径下的合集文件夹,合集文件夹格式为 <code>MIX123456789_mark_合集作品</code> 或者 <code>MIX123456789_合集标题_合集作品</code></p>
<h3>获取直播拉流地址(TikTok)</h3>
<p>输入直播链接,不支持已结束的直播。</p>
<p>支持链接格式:</p>
<ul>
<li><code>https://vt.tiktok.com/分享码/</code></li>
<li><code>https://www.tiktok.com/@TikTok号/live</code></li>
</ul>
<p>下载说明:</p>
<ul>
<li>程序会询问用户是否下载直播视频,支持同时下载多个直播视频。</li>
<li>程序调用 <code>ffmpeg</code> 下载直播时,关闭 DouK-Downloader 不会影响直播下载。</li>
<li><del>程序调用内置下载器下载直播时,需要保持 DouK-Downloader 运行直到直播结束。</del></li>
<li>程序询问是否下载直播时,输入直播清晰度或者对应序号即可下载,例如:下载最高清晰度输入 <code>FULL_HD1</code> 或者 <code>1</code> 均可。</li>
<li><del>程序调用内置下载器下载的直播文件,视频时长会显示为直播总时长,实际视频内容从下载时间开始,靠后部分的片段无法播放。</del></li>
<li>直播视频会下载至 <code>root</code> 参数路径下的 <code>Live</code> 文件夹。</li>
<li>按下 <code>Ctrl + C</code> 终止程序或 <code>ffmpeg</code> 并不会导致已下载文件丢失或损坏,但无法继续下载。</li>
</ul>
<h3><del>批量下载视频原画(TikTok)</del></h3>
<p><strong>注意:本功能为实验性功能,依赖第三方 API 服务,可能不稳定或存在限制!</strong></p>
<ol>
<li>手动输入待采集的作品链接。</li>
<li>输入文本文档路径,读取文件包含的作品链接。</li>
</ol>
<p>支持链接格式:</p>
<ul>
<li><code>https://vm.tiktok.com/分享码/</code></li>
<li><code>https://www.tiktok.com/@TikTok号/video/作品ID</code></li>
</ul>
<p>作品会下载至 <code>root</code> 参数和 <code>folder_name</code> 参数拼接成的文件夹。</p>
<h2>后台监听模式</h2>
<h3>剪贴板监听下载</h3>
<p>程序会自动检测并提取剪贴板中的抖音和 TikTok 作品链接,并自动下载作品文件;如需关闭,请按下 Ctrl+C,或将剪贴板内容设置为“close”以停止监听!</p>
<h2>Web API 接口模式</h2>
<p>启动服务器,提供 API 调用功能;支持局域网远程访问,可以部署至私有服务器或者公开服务器,远程部署建议设置参数验证,防止恶意请求!</p>
<p>默认禁用局域网访问,如需开启,请修改 <code>src/custom/static.py</code> 文件的 <code>SERVER_HOST</code> 变量。</p>
<p><strong>启动该模式后,访问 <code>http://127.0.0.1:5555/docs</code> 或者 <code>http://127.0.0.1:5555/redoc</code> 可以查阅自动生成的文档!</strong></p>
<h3>API 调用示例代码</h3>
<pre>
from httpx import post
from rich import print


def demo():
headers = {"token": ""}
data = {
"detail_id": "0123456789",
"pages": 2,
}
api = "http://127.0.0.1:5555/douyin/comment"
response = post(api, json=data, headers=headers)
print(response.json())

demo()
</pre>
<h2>Web UI 交互模式</h2>
<p><b>项目代码已重构,该模式代码尚未更新,未来开发完成重新开放!</b></p>
<h2>启用/禁用作品下载记录</h2>
<ul>
<li>启用该功能:程序会记录下载成功的作品 ID,如果对作品文件进行移动、重命名或者删除操作,程序不会重复下载该作品,如果想要重新下载该作品,需要删除记录数据中对应的作品 ID。</li>
<li>禁用该功能:程序会在下载文件前检测文件是否存在,如果文件存在会自动跳过下载该作品,如果对作品文件进行移动、重命名或者删除操作,程序将会重新下载该作品。</li>
</ul>
<p>数据路径: <code>./Volume/DouK-Downloader.db</code> 的 <code>download_data</code> 数据表。</p>
<h2>删除指定下载记录</h2>
<p>输入作品 ID 或者作品完整链接(多个作品之间使用空格分隔,支持混合输入),删除作品下载记录中对应的数据,如果输入 <code>all</code>,代表清空作品下载记录数据!</p>
<h2>启用/禁用运行日志记录</h2>
<p>是否将程序运行日志记录保存到文件,默认关闭,日志文件保存路径:<code>./Volume/Log</code></p>
<p>如果在使用过程中发现程序 Bug,可以及时告知作者,并附上日志文件,日志记录有助于作者分析 Bug 原因和修复 Bug。</p>
<h2>检查程序版本更新</h2>
<p>程序会向 <code>https://github.com/JoeanAmier/TikTokDownloader/releases/latest</code>
发送请求获取最新 <code>Releases</code> 版本号,并提示是否存在新版本。</p>
<p>如果检查新版本失败,可能是访问 GitHub 超时,并非功能异常;如果存在新版本会提示新版本的 <code>URL</code> 地址,不会自动下载更新。</p>
<h1>其他功能说明</h1>
<h2>单次输入多个链接</h2>
<p><code>批量下载账号作品</code>、<code>批量下载链接作品</code>、<code>获取直播拉流地址</code>、<code>采集作品评论数据</code>、<code>批量下载合集作品</code>、<code>采集账号详细数据</code>
功能支持单次输入多个链接,实现批量下载 / 提取功能;支持完整链接与分享链接混合输入;输入多个链接时,需要使用空格分隔;无需对复制的链接进行额外处理,程序会自动提取输入文本中的有效链接。</p>
<h2 id="mark">账号/合集标识说明</h2>
<h3>标识设置规则</h3>
<ul>
<li><code>name_format</code> 参数中没有使用 <code>nickname</code> 时,<code>mark</code> 设置没有限制。</li>
<li><code>name_format</code> 参数中使用了 <code>nickname</code> 时,<code>mark</code> 与 <code>nickname</code> 不能设置为包含关系的字符串。</li>
</ul>
<p><strong>标识示例:</strong></p>
<ul>
<li>✔️ <code>nickname</code>:ABC,<code>mark</code>:DEF</li>
<li>✔️ <code>nickname</code>:ABC,<code>mark</code>:BCD</li>
<li>❌ <code>nickname</code>:ABC,<code>mark</code>:AB</li>
<li>❌ <code>nickname</code>:BC,<code>mark</code>:ABC</li>
</ul>
<h3>账号标识说明</h3>
<ul>
<li>账号标识 <code>mark</code> 参数相当于账号备注,便于用户识别账号作品文件夹,避免账号昵称修改导致无法识别已下载作品问题。</li>
<li><code>批量下载账号作品</code> 模式下,如果设置了 <code>mark</code> 参数,下载的作品将会保存至 <code>UID123456789_mark_发布作品</code>
或 <code>UID123456789_mark_喜欢作品</code> 文件夹内。</li>
<li><code>批量下载账号作品</code> 模式下,如果 <code>mark</code>
参数设置为空字符串,程序将会使用账号昵称作为账号标识,下载的作品将会保存至 <code>UID123456789_账号昵称_发布作品</code>
或 <code>UID123456789_账号昵称_喜欢作品</code> 文件夹内。</li>
</ul>
<h3>合集标识说明</h3>
<p>与账号标识作用一致。</p>
<h3>如何修改标识</h3>
<p><strong>修改账号标识:</strong> 修改 <code>accounts_urls</code> 或 <code>accounts_urls_tiktok</code> 的 <code>mark</code> 参数,再次运行 <code>批量下载账号作品</code> 模式,程序会自动应用新的账号标识。</p>
<p><strong>修改合集标识:</strong> 修改 <code>mix_urls</code> 或 <code>mix_urls_tiktok</code> 的 <code>mark</code> 参数,再次运行 <code>批量下载合集作品</code> 模式,程序会自动应用新的账号标识。</p>
<h2>账号昵称/合集标题自动更新</h2>
<p>在 <code>批量下载账号作品</code> 和 <code>批量下载合集作品</code> 模式下,程序会自动判断账号昵称/合集标题是否发生变化,如果发生变化,程序会自动识别已下载作品文件名称中的账号昵称/合集标题,并修改至最新账号昵称/合集标题。</p>
<p>程序会优先使用账号标识/合集标识进行更新处理,如果账号标识/合集标识为空字符串,程序会自动使用账号昵称/合集标题进行更新处理。</p>
<h3>映射缓存数据</h3>
<p><strong>数据路径: <code>./Volume/DouK-Downloader.db</code> 的 <code>mapping_data</code> 数据表;</strong>
用于记录账号 / 合集标识和账号昵称,当账号 / 合集标识或账号昵称发生变化时,程序会对相应的文件夹和文件进行重命名更新处理。</p>
<p><strong>缓存数据仅供程序读取和修改,不建议手动编辑数据内容。</strong></p>

# 构建可执行文件指南

本指南将引导您通过 Fork 本仓库并执行 GitHub Actions 自动完成基于最新源码的程序构建和打包!

---

## 使用步骤

### 1. Fork 本仓库

1. 点击项目仓库右上角的 **Fork** 按钮,将本仓库 Fork 到您的个人 GitHub 账户中
2. 您的 Fork 仓库地址将类似于:`https://github.com/your-username/this-repo`

---

### 2. 启用 GitHub Actions

1. 前往您 Fork 的仓库页面
2. 点击顶部的 **Settings** 选项卡
3. 点击右侧的 **Actions** 选项卡
4. 点击 **General** 选项
5. 在 **Actions permissions** 下,选择 **Allow all actions and reusable workflows** 选项,点击 **Save** 按钮

---

### 3. 手动触发打包流程

1. 在您 Fork 的仓库中,点击顶部的 **Actions** 选项卡
2. 找到名为 **手动构建可执行文件** 的工作流
3. 点击右侧的 **Run workflow** 按钮:
    - 选择 **master** 或者 **develop** 分支
    - 点击 **Run workflow**

---

### 4. 查看打包进度

1. 在 **Actions** 页面中,您可以看到触发的工作流运行记录
2. 点击运行记录,查看详细的日志以了解打包进度和状态

---

### 5. 下载打包结果

1. 打包完成后,进入对应的运行记录页面
2. 在页面底部的 **Artifacts** 部分,您将看到打包的结果文件
3. 点击下载并保存到本地,即可获得打包好的程序

---

## 注意事项

1. **资源使用**:
    - Actions 的运行环境由 GitHub 免费提供,普通用户每月有一定的免费使用额度(2000 分钟)

2. **代码修改**:
    - 您可以自由修改 Fork 仓库中的代码以定制程序打包流程
    - 修改后重新触发打包流程,您将得到自定义的构建版本

3. **与主仓库保持同步**:
    - 如果主仓库更新了代码或工作流,建议您定期同步 Fork 仓库以获取最新功能和修复

---

## Actions 常见问题

### Q1: 为什么我无法触发工作流?

A: 请确认您已按照步骤 **启用 Actions**,否则 GitHub 会禁止运行工作流

### Q2: 打包流程失败怎么办?

A:

- 检查运行日志,了解失败原因
- 确保代码没有语法错误或依赖问题
- 如果问题仍未解决,可以在本仓库的 [Issues 页面](https://github.com/JoeanAmier/TikTokDownloader/issues) 提出问题

### Q3: 我可以直接使用主仓库的 Actions 吗?

A: 由于权限限制,您无法直接触发主仓库的 Actions。请通过 Fork 仓库的方式执行打包流程

<h1>常见问题与解决方案</h1>
<h2>响应内容不是有效的 JSON 数据</h2>
<p>可能是 Cookie 无效或者接口失效;请尝试清除 DNS 缓存,更新 Cookie,如果仍然无法解决,可能是接口失效,请考虑向作者反馈!</p>
<h2 id="twc">获取 ttwid 参数失败</h2>
<p>TikTok 平台的 Cookie ttwid 值无效;可能是当前账号被风控,请考虑更换账号,或者尝试设置 <code>twc_tiktok</code> 参数。</p>
<p><code>twc_tiktok</code> 参数设置教程:</p>
<ul>
<li>以无痕模式打开浏览器</li>
<li>按 <code>F12</code> 打开开发人员工具</li>
<li>选择 <code>网络</code> 选项卡</li>
<li>访问 <code>https://www.tiktok.com/</code></li>
<li>在 <code>筛选器</code> 输入框输入 <code>ttwid</code></li>
<li>在开发人员工具窗口选择任意一个数据包(如果无数据包,刷新网页)</li>
<li>检查 <code>响应标头</code> -> <code>Set-Cookie</code></li>
<li>复制 <code>ttwid=XXX</code> 内容</li>
<li>粘贴至配置文件的 <code>twc_tiktok</code> 参数</li>
</ul>
<p><code>Set-Cookie</code> 的内容格式为:<code>ttwid=XXX; Path=/; Domain=tiktok.com; Max-Age=31536000; HttpOnly; Secure; SameSite=None</code>,复制时只需要复制 <code>ttwid=XXX</code> 部分,而不是复制全部内容!</p>
<h2>采集数据而不下载文件</h2>
<p>将配置文件的 <code>download</code> 参数设置为 <code>false</code>,并设置 <code>storage_format</code> 参数,程序将不会下载任何文件,仅采集数据。</p>
<h2>请求超时:timed out</h2>
<p>网络异常;如果您的网络需要使用代理才能访问 TikTok,请在配置文件设置 <code>proxy</code> 参数!</p>
<h2>self 获取账号信息失败</h2>
<p>请把配置文件的 <code>owner_url</code> 参数修改为实际的抖音主页链接,获取方式请查阅 <a href="https://github.com/JoeanAmier/TikTokDownloader/issues/416">issue</a></p>
<h1>免责声明</h1>
<ol>
<li>使用者对本项目的使用由使用者自行决定,并自行承担风险。作者对使用者使用本项目所产生的任何损失、责任、或风险概不负责。</li>
<li>本项目的作者提供的代码和功能是基于现有知识和技术的开发成果。作者按现有技术水平努力确保代码的正确性和安全性,但不保证代码完全没有错误或缺陷。</li>
<li>本项目依赖的所有第三方库、插件或服务各自遵循其原始开源或商业许可,使用者需自行查阅并遵守相应协议,作者不对第三方组件的稳定性、安全性及合规性承担任何责任。</li>
<li>使用者在使用本项目时必须严格遵守 <a href="https://github.com/JoeanAmier/TikTokDownloader/blob/master/LICENSE">GNU
    General Public License v3.0</a> 的要求,并在适当的地方注明使用了 <a
        href="https://github.com/JoeanAmier/TikTokDownloader/blob/master/LICENSE">GNU General Public License
    v3.0</a> 的代码。
</li>
<li>使用者在使用本项目的代码和功能时,必须自行研究相关法律法规,并确保其使用行为合法合规。任何因违反法律法规而导致的法律责任和风险,均由使用者自行承担。</li>
<li>使用者不得使用本工具从事任何侵犯知识产权的行为,包括但不限于未经授权下载、传播受版权保护的内容,开发者不参与、不支持、不认可任何非法内容的获取或分发。</li>
<li>本项目不对使用者涉及的数据收集、存储、传输等处理活动的合规性承担责任。使用者应自行遵守相关法律法规,确保处理行为合法正当;因违规操作导致的法律责任由使用者自行承担。</li>
<li>使用者在任何情况下均不得将本项目的作者、贡献者或其他相关方与使用者的使用行为联系起来,或要求其对使用者使用本项目所产生的任何损失或损害负责。</li>
<li>本项目的作者不会提供 DouK-Downloader 项目的付费版本,也不会提供与 DouK-Downloader 项目相关的任何商业服务。</li>
<li>基于本项目进行的任何二次开发、修改或编译的程序与原创作者无关,原创作者不承担与二次开发行为或其结果相关的任何责任,使用者应自行对因二次开发可能带来的各种情况负全部责任。</li>
<li>本项目不授予使用者任何专利许可;若使用本项目导致专利纠纷或侵权,使用者自行承担全部风险和责任。未经作者或权利人书面授权,不得使用本项目进行任何商业宣传、推广或再授权。</li>
<li>作者保留随时终止向任何违反本声明的使用者提供服务的权利,并可能要求其销毁已获取的代码及衍生作品。</li>
<li>作者保留在不另行通知的情况下更新本声明的权利,使用者持续使用即视为接受修订后的条款。</li>
</ol>
<b>在使用本项目的代码和功能之前,请您认真考虑并接受以上免责声明。如果您对上述声明有任何疑问或不同意,请不要使用本项目的代码和功能。如果您使用了本项目的代码和功能,则视为您已完全理解并接受上述免责声明,并自愿承担使用本项目的一切风险和后果。</b>


================================================
FILE: docs/Release_Notes.md
================================================
**更新内容:**

1. API 模式搜索接口增加 `offset` 和 `count` 参数
2. 修复部分 TikTok 账号提取 sec_user_id 失败的问题
3. 修复 API 模式搜索接口多页数据报错的问题
4. 修复 API 模式搜索接口结果为空报错的问题
5. 修复 TikTok 平台批量下载账号作品功能
6. 修复 TikTok 平台批量下载合集作品功能
7. TikTok 平台新增 X-Gnarly 请求参数
8. 优化提取 secUid 的正则表达式
9. 服务器模式默认启用局域网访问
10. 修复请求参数编码错误的问题
11. 修复提取作品 ID 失败的问题
12. 更新数据接口请求参数
13. 更新项目英语翻译
14. 修正项目功能描述
15. 修复其他已知问题


================================================
FILE: license
================================================
                    GNU GENERAL PUBLIC LICENSE
                       Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU General Public License is a free, copyleft license for
software and other kinds of works.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.  We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors.  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights.  Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received.  You must make sure that they, too, receive
or can get the source code.  And you must show them these terms so they
know their rights.

  Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.

  For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software.  For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.

  Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so.  This is fundamentally incompatible with the aim of
protecting users' freedom to change the software.  The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable.  Therefore, we
have designed this version of the GPL to prohibit the practice for those
products.  If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.

  Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary.  To prevent this, the GPL assures that
patents cannot be used to render the program non-free.

  The precise terms and conditions for copying, distribution and
modification follow.

                       TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this
License.  Each licensee is addressed as "you".  "Licensees" and
"recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based
on the Program.

  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work
for making modifications to it.  "Object code" means any non-source
form of a work.

  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

  The Corresponding Source for a work in source code form is that
same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

  13. Use with the GNU Affero General Public License.

  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

  Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

  Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

  If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:

    <program>  Copyright (C) <year>  <name of author>
    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".

  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.

  The GNU General Public License does not permit incorporating your program
into proprietary programs.  If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.  But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.


================================================
FILE: locale/README.md
================================================
# 命令参考

**运行命令前,确保已经安装了 `gettext` 软件包,并配置好环境变量。**

**Before running the command, ensure that the `gettext` package is installed and the environment variables are properly
configured.**

* `xgettext --files-from=py_files.txt -d tk -o tk.pot`
* `mkdir zh_CN\LC_MESSAGES`
* `msginit -l zh_CN -o zh_CN/LC_MESSAGES/tk.po -i tk.pot`
* `mkdir en_US\LC_MESSAGES`
* `msginit -l en_US -o en_US/LC_MESSAGES/tk.po -i tk.pot`
* `msgmerge -U zh_CN/LC_MESSAGES/tk.po tk.pot`
* `msgmerge -U en_US/LC_MESSAGES/tk.po tk.pot`

# 翻译贡献指南

* 如果想要贡献支持更多语言,请在终端切换至 `locale` 文件夹,运行命令 `msginit -l 语言代码 -o 语言代码/LC_MESSAGES/tk.po -i tk.pot`
  生成 po 文件并编辑翻译。
* 如果想要贡献改进翻译结果,请直接编辑 `tk.po` 文件内容。
* 仅需提交 `tk.po` 文件,作者会转换格式并合并。

# Translation Contribution Guide

* If you want to contribute support for more languages, please switch to the `locale` folder in the terminal and run the
  command `msginit -l language_code -o language_code/LC_MESSAGES/tk.po -i tk.pot` to generate the po file and edit the
  translation.
* If you want to contribute to improving the translation, please directly edit the content of the `tk.po` file.
* Only the `tk.po` file needs to be submitted, and the author will convert the format and merge it.


================================================
FILE: locale/en_US/LC_MESSAGES/tk.po
================================================
# English translations for DouK-Downloader package.
# Copyright (C) 2024 THE DouK-Downloader'S COPYRIGHT HOLDER
# This file is distributed under the same license as the DouK-Downloader package.
# FIRST AUTHOR <yonglelolu@foxmail.com>, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: DouK-Downloader 5.8\n"
"Report-Msgid-Bugs-To: <yonglelolu@foxmail.com>\n"
"POT-Creation-Date: 2025-11-04 10:48+0800\n"
"PO-Revision-Date: 2024-12-22 21:46+0800\n"
"Last-Translator: <yonglelolu@foxmail.com>\n"
"Language-Team: English\n"
"Language: en_US\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_monitor.py:41
msgid ""
"程序会自动检测并提取剪贴板中的抖音和 TikTok 作品链接,并自动下载作品文件;如"
"需关闭,请按下 Ctrl+C,或将剪贴板内容设置为“close”以停止监听!"
msgstr ""
"The program will automatically detect and extract TikTok and DouYin video "
"links from the clipboard, then download the video files. To turn it off, "
"press Ctrl+C or set the clipboard content to \"close\" to stop monitoring!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_monitor.py:129
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:941
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:968
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1288
#, python-brace-format
msgid "{url} 提取作品 ID 失败"
msgstr "Failed to extract works ID from {url}"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:50
msgid "验证失败!"
msgstr "Verification failed!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:106
msgid "访问项目 GitHub 仓库"
msgstr "Visit the project's GitHub repository"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:107
msgid "重定向至项目 GitHub 仓库主页"
msgstr "Redirect to the project's GitHub repository homepage"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:108
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:123
msgid "项目"
msgstr "Project"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:115
msgid "测试令牌有效性"
msgstr "Test token validity"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:128
msgid "验证成功!"
msgstr "Verification successful!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:135
msgid "更新项目全局配置"
msgstr "Update project global configuration"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:145
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:158
msgid "配置"
msgstr "Configuration"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:156
msgid "获取项目全局配置"
msgstr "Retrieve project global configuration"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:157
msgid "返回项目全部配置参数"
msgstr "Return all configuration parameters of the project"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:166
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:511
msgid "获取分享链接重定向的完整链接"
msgstr "Retrieve the complete link for the shared link redirect"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:175
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:206
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:233
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:259
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:299
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:342
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:379
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:419
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:449
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:477
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:501
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\parameter.py:190
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\parameter.py:444
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\parameter.py:885
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\parameter.py:961
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\module\cookie.py:26
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\tools\browser.py:43
msgid "抖音"
msgstr "DouYin"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:183
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:528
msgid "请求链接成功!"
msgstr "Request link successful!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:188
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:533
msgid "请求链接失败!"
msgstr "Request link failed!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:195
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:540
msgid "获取单个作品数据"
msgstr "Retrieve data for a single works"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:216
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:561
msgid "获取账号作品数据"
msgstr "Retrieve account works data"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:243
msgid "获取合集作品数据"
msgstr "Retrieve mix works data"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:269
msgid "参数错误!"
msgstr "Parameter error!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:288
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:622
msgid "获取直播数据"
msgstr "Retrieve live stream data"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:326
msgid "获取作品评论数据"
msgstr "Retrieve work comments data"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:364
msgid "获取评论回复数据"
msgstr "Retrieve comment replies data"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:398
msgid "获取综合搜索数据"
msgstr "Retrieve general search data"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:429
msgid "获取视频搜索数据"
msgstr "Retrieve video search data"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:459
msgid "获取用户搜索数据"
msgstr "Retrieve user search data"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:487
msgid "获取直播搜索数据"
msgstr "Retrieve live search data"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:588
msgid "获取合辑作品数据"
msgstr "Retrieve mix works data"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:656
msgid "搜索结果为空!"
msgstr "The search result is empty!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:709
msgid "获取数据成功!"
msgstr "Successfully retrieved data!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_server.py:720
msgid "获取数据失败!"
msgstr "Failed to retrieve data!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:71
msgid ""
"未设置 storage_format 参数,无法正常使用该功能,详细说明请查阅项目文档!"
msgstr ""
"The `storage_format` parameter is not set, so this feature cannot be used "
"properly. Please refer to the project documentation for detailed information!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:86
msgid "抖音 Cookie"
msgstr "DouYin Cookie"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:90
#, python-brace-format
msgid "{tip} 未登录,无法使用该功能,详细说明请查阅项目文档!"
msgstr ""
"{tip} Not logged in, unable to use this feature. Please refer to the project "
"documentation for detailed information!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:143
msgid "批量下载账号作品(抖音)"
msgstr "Batch Download Account Works (DouYin)"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:147
msgid "批量下载链接作品(抖音)"
msgstr "Batch Download Works from Links (DouYin)"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:151
msgid "获取直播拉流地址(抖音)"
msgstr "Get live stream pull URL (DouYin)"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:155
msgid "采集作品评论数据(抖音)"
msgstr "Collect Works Comment Data (DouYin)"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:159
msgid "批量下载合集作品(抖音)"
msgstr "Batch Download Mix Works (DouYin)"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:163
msgid "采集账号详细数据(抖音)"
msgstr "Collect Account Detailed Data (DouYin)"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:167
msgid "采集搜索结果数据(抖音)"
msgstr "Collect Search Result Data (DouYin)"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:171
msgid "采集抖音热榜数据(抖音)"
msgstr "Collect DouYin Hot Board Data (DouYin)"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:176
msgid "批量下载收藏作品(抖音)"
msgstr "Batch Download Favorites Works (DouYin)"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:180
msgid "批量下载收藏音乐(抖音)"
msgstr "Batch Download Favorites Music (DouYin)"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:185
msgid "批量下载收藏夹作品(抖音)"
msgstr "Batch Download Collections Works (DouYin)"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:189
msgid "批量下载账号作品(TikTok)"
msgstr "Batch Download Account Works (TikTok)"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:193
msgid "批量下载链接作品(TikTok)"
msgstr "Batch Download Works from Links (TikTok)"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:197
msgid "批量下载合集作品(TikTok)"
msgstr "Batch Download Mix Works (TikTok)"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:201
msgid "获取直播拉流地址(TikTok)"
msgstr "Get live stream pull URL (TikTok)"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:206
msgid "批量下载视频原画(TikTok)"
msgstr "Batch Download Video original file (TikTok)"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:211
msgid "使用 accounts_urls 参数的账号链接(推荐)"
msgstr "Account links using the `accounts_urls` parameter (recommended)"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:212
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:220
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:236
msgid "手动输入待采集的账号链接"
msgstr "Manually enter the account links to be collected"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:213
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:221
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:237
msgid "从文本文档读取待采集的账号链接"
msgstr "Read account links to be collected from a text file"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:217
msgid "使用 accounts_urls_tiktok 参数的账号链接(推荐)"
msgstr "Account links using the `accounts_urls_tiktok` parameter (recommended)"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:224
msgid "使用 mix_urls 参数的合集链接(推荐)"
msgstr "Mix links using the `mix_urls` parameter (recommended)"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:225
msgid "获取当前账号收藏合集列表"
msgstr "Retrieve the current account's Collections Mix list"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:226
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:231
msgid "手动输入待采集的合集/作品链接"
msgstr "Manually enter the Mix/Works links to be collected"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:227
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:232
msgid "从文本文档读取待采集的合集/作品链接"
msgstr "Read the Mix/Works links to be collected from a text file"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:230
msgid "使用 mix_urls_tiktok 参数的合集链接(推荐)"
msgstr "Mix links using the `mix_urls_tiktok` parameter (recommended)"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:235
msgid "使用 accounts_urls 参数的账号链接"
msgstr "Account links using the `accounts_urls` parameter"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:240
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:244
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:248
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:252
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:256
msgid "手动输入待采集的作品链接"
msgstr "Manually enter the works links to be collected"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:241
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:245
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:249
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:253
msgid "从文本文档读取待采集的作品链接"
msgstr "Read the works links to be collected from a text file"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:261
msgid "综合搜索数据采集"
msgstr "General Search"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:265
msgid "视频搜索数据采集"
msgstr "Video Search"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:269
msgid "用户搜索数据采集"
msgstr "User Search"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:273
msgid "直播搜索数据采集"
msgstr "Live Search"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:283
#, python-brace-format
msgid "请输入{tip}链接: "
msgstr "Please enter the {tip} link:"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:296
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:322
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:330
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1821
msgid "请选择账号链接来源"
msgstr "Please select the account link source"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:300
msgid "已退出批量下载账号作品(TikTok)模式"
msgstr "Exited Batch Download Account Works (TikTok) mode"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:302
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:415
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:535
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\interface\user.py:25
msgid "账号"
msgstr "Account"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:306
#, python-brace-format
msgid "程序共处理 {0} 个{1},成功 {2} 个,失败 {3} 个,耗时 {4} 分钟 {5} 秒"
msgstr ""
"The program processed a total of {0} {1}, with {2} successes, {3} failures, "
"and a duration of {4} minutes {5} seconds."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:326
msgid "已退出批量下载账号作品(抖音)模式"
msgstr "Exited Batch Download Account Works (DouYin) mode"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:382
#, python-brace-format
msgid "共有 {count} 个账号的作品等待下载"
msgstr "There are {count} accounts' works waiting to be downloaded"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:393
#, python-brace-format
msgid ""
"配置文件 {name} 参数的 url {url} 提取 sec_user_id 失败,错误配置:{data}"
msgstr ""
"Failed to extract sec_user_id from the url {url} in the configuration file "
"{name}, error configuration: {data}"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:434
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:451
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1743
msgid "账号主页"
msgstr "Account Page"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:438
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:455
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1747
#, python-brace-format
msgid "{url} 提取账号 sec_user_id 失败"
msgstr "Failed to extract account sec_user_id from {url}"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:470
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:508
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1776
msgid "从文本文档提取账号 sec_user_id 失败"
msgstr "Failed to extract account sec_user_id from the text file"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:556
#, python-brace-format
msgid "开始处理第 {index} 个账号"
msgstr "Start processing the {index}th account"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:558
msgid "开始处理账号"
msgstr "Starting to process the account"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:571
#, python-brace-format
msgid "{sec_user_id} 获取账号信息失败,请检查 Cookie 登录状态!"
msgstr ""
"Failed to retrieve account information for {sec_user_id}, please check the "
"Cookie login status!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:582
msgid ""
"如果账号发布作品均为共创作品且该账号均不是作品作者时,请配置已登录的 Cookie "
"后重新运行程序,其余情况请无视该提示!"
msgstr ""
"If all works published by the account are co-created works and the account "
"is not the author of any work, please configure a logged-in Cookie and run "
"the program again. Ignore this message in all other cases!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:730
msgid "开始提取作品数据"
msgstr "Starting to extract works data"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:743
msgid "提取账号或合集信息发生错误!"
msgstr "An error occurred while extracting account or collection information!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:825
msgid "发布作品"
msgstr "Posts"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:827
msgid "喜欢作品"
msgstr "Liked"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:829
msgid "收藏作品"
msgstr "Favorites"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:831
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\interface\mix.py:35
msgid "合集作品"
msgstr "Mix"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:833
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\interface\collects.py:89
msgid "收藏夹作品"
msgstr "Collections"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:844
#, python-brace-format
msgid "昵称/标题:{name};标识:{mark};ID:{id}"
msgstr "Nickname/Title: {name}; Mark: {mark}; ID: {id}"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:884
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:918
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1272
msgid "请选择作品链接来源"
msgstr "Please select the works link source"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:888
msgid "已退出批量下载链接作品(抖音)模式"
msgstr "Exited Batch Download Works from Links (DouYin) mode"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:898
msgid "已退出批量下载链接作品(TikTok)模式"
msgstr "Exited Batch Download Works from Links (TikTok) mode"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:905
msgid "注意:本功能为实验性功能,依赖第三方 API 服务,可能不稳定或存在限制!"
msgstr ""
"Note: This feature is experimental and relies on unofficial API services, "
"which may be unstable or have limitations!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:911
msgid "已退出批量下载视频原画(TikTok)模式"
msgstr "Exited Batch Download Video original file (TikTok) mode"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:938
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:965
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1283
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\interface\detail.py:24
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\interface\detail_tiktok.py:24
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\interface\slides.py:26
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\module\tiktok_unofficial.py:38
msgid "作品"
msgstr "Works"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:944
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:971
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1017
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1291
#, python-brace-format
msgid "共提取到 {count} 个作品,开始处理!"
msgstr "Successfully extracted {count} works, starting to process!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:984
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1005
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1014
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1312
msgid "从文本文档提取作品 ID 失败"
msgstr "Failed to extract works ID from the text file"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1093
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\downloader\download.py:320
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\downloader\download.py:323
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\downloader\download.py:405
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\downloader\download.py:749
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\extract\extractor.py:434
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\extract\extractor.py:453
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\extract\extractor.py:467
msgid "图集"
msgstr "Image"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1095
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\downloader\download.py:326
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\downloader\download.py:329
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\downloader\download.py:456
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\downloader\download.py:751
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\extract\extractor.py:351
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\extract\extractor.py:365
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\extract\extractor.py:480
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\extract\extractor.py:570
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\interface\search.py:102
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\module\tiktok_unofficial.py:116
msgid "视频"
msgstr "Video"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1105
msgid "程序未检测到有效的 ffmpeg,不支持直播下载功能!"
msgstr ""
"The program did not detect a valid ffmpeg tool, live streaming download "
"functionality is not supported!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1109
msgid "请选择下载清晰度(输入清晰度或者对应序号,直接回车代表不下载): "
msgstr ""
"Please select the download resolution (enter the resolution or corresponding "
"number, press Enter to skip download):"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1116
msgid "未输入有效的清晰度或者序号,跳过下载!"
msgstr "No valid resolution or serial number entered, skipping download!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1149
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1164
msgid "直播"
msgstr "Live"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1154
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1171
msgid "获取直播数据失败"
msgstr "Failed to retrieve live stream data"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1158
msgid "已退出获取直播拉流地址(抖音)模式"
msgstr "Exited Get DouYin live stream pull URL mode"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1167
msgid "{} 提取直播 ID 失败"
msgstr "Failed to extract live stream ID from {}"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1181
msgid "已退出获取直播拉流地址(TikTok)模式"
msgstr "Exited Get TikTok live stream pull URL mode"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1197
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1214
msgid "直播标题:"
msgstr "Live Stream Title:"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1198
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1215
msgid "主播昵称:"
msgstr "Presenter Nickname:"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1199
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1217
msgid "在线观众:"
msgstr "Online Viewers:"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1200
msgid "观看次数:"
msgstr "View Count:"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1202
msgid "当前直播已结束!"
msgstr "The current live stream has ended!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1216
msgid "开播时间:"
msgstr "Start Time:"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1218
msgid "点赞次数:"
msgstr "Like Count:"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1223
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1242
msgid "FLV 拉流地址: "
msgstr "FLV Stream push URL:"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1226
msgid "M3U8 拉流地址: "
msgstr "M3U8 Stream push URL:"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1264
msgid "已退出采集作品评论数据(TikTok)模式"
msgstr "Exited Collect Works Comment Data (TikTok) mode"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1276
msgid "已退出采集作品评论数据(抖音)模式)"
msgstr "Exited Collect Works Comment Data (DouYin) mode"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1363
#, python-brace-format
msgid "作品评论数据已储存至 {filename}"
msgstr "Works comment data has been saved to {filename}"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1364
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1374
#, python-brace-format
msgid "作品{id}_评论数据"
msgstr "Works{id}_CommentData"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1368
msgid "采集评论数据失败"
msgstr "Failed to collect comment data"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1423
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1434
msgid "请选择合集链接来源"
msgstr "Please select the Mix link source"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1427
msgid "已退出批量下载合集作品(抖音)模式"
msgstr "Exited Batch Download Mix Works (DouYin) mode"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1438
msgid "已退出批量下载合集作品(TikTok)模式"
msgstr "Exited Batch Download Mix Works (TikTok) mode"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1455
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1470
msgid "合集或作品"
msgstr "Mix or Works"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1459
#, python-brace-format
msgid "{url} 获取作品 ID 或合集 ID 失败"
msgstr "Failed to retrieve Works ID or Mix ID from {url}"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1473
#, python-brace-format
msgid "{url} 获取合集 ID 失败"
msgstr "Failed to retrieve Mix ID from {url}"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1502
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1509
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\interface\collects.py:150
msgid "收藏合集"
msgstr "Collections Mix"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1513
#, python-brace-format
msgid "{text}列表:"
msgstr "{text} List:"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1518
#, python-brace-format
msgid ""
"请输入需要下载的{item}序号(多个序号使用空格分隔,输入 ALL 下载全部{item}):"
msgstr ""
"Please enter the serial numbers of the {item} to download (separate multiple "
"numbers with spaces, enter ALL to download all {item}):"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1532
#, python-brace-format
msgid "{text}序号输入错误!"
msgstr "Incorrect {text} serial number input!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1540
msgid "从文本文档提取作品 ID 或合集 ID 失败"
msgstr "Failed to extract Works ID or Mix ID from the text file"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1550
msgid "从文本文档提取合集 ID 失败"
msgstr "Failed to extract Mix ID from the text file"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1590
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1650
msgid "合集"
msgstr "Mix"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1626
#, python-brace-format
msgid ""
"配置文件 {name} 参数的 url {url} 获取作品 ID 或合集 ID 失败,错误配置:{data}"
msgstr ""
"Failed to obtain the work ID or collection ID from the url {url} in the "
"configuration file {name}, error configuration: {data}"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1668
#, python-brace-format
msgid "开始处理第 {index} 个合集"
msgstr "Starting to process the {index}th Mix"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1670
msgid "开始处理合集"
msgstr "Starting to process the Mix"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1704
msgid "采集合集作品数据失败"
msgstr "Failed to collect Mix Works data"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1730
#, python-brace-format
msgid "配置文件 accounts_urls 参数第 {index} 条数据的 url 无效"
msgstr ""
"The URL in parameter {index} of the `accounts_urls` in the configuration "
"file is invalid"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1754
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\cli_edition\write.py:40
msgid "请输入文本文档路径:"
msgstr "Please enter the text file path:"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1761
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\cli_edition\write.py:47
#, python-brace-format
msgid "{path} 文件读取异常: {error}"
msgstr "File read error at {path}: {error}"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1764
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\cli_edition\write.py:50
#, python-brace-format
msgid "{path} 文件不存在!"
msgstr "The file {path} does not exist!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1788
#, python-brace-format
msgid "正在获取账号 {sec_user_id} 的数据"
msgstr "正在获取账号 {sec_user_id} 的数据"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1815
msgid "账号数据已保存至文件"
msgstr "Account data has been saved to the file."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1825
msgid "已退出采集账号详细数据模式"
msgstr "Exited Collect Account Detailed Data mode."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1832
#, python-brace-format
msgid "请输入搜索参数;参数之间使用两个空格分隔({field}):\n"
msgstr ""
"Please enter search parameters; Separate parameters with two spaces "
"({field}): \n"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1855
msgid "请选择搜索模式"
msgstr "Please choose a search mode"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1934
msgid "搜索结果为空"
msgstr "The search result is empty"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:1956
msgid "搜索数据"
msgstr "Search_Data"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:2023
#, python-brace-format
msgid "搜索数据已保存至 {name}"
msgstr "Search data has been saved to {name}"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:2032
msgid "已退出采集抖音热榜数据(抖音)模式"
msgstr "Exited Collect DouYin Hot Board Data mode."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:2052
#, python-brace-format
msgid "热榜数据_{time}_{name}"
msgstr "HotBoardData_{time}_{name}"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:2066
#, python-brace-format
msgid "热榜数据已储存至: 热榜数据_{time} + 榜单类型"
msgstr "Hot Board Data has been saved to: HotBoardData_{time} + Board Type"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:2081
msgid "已退出批量下载收藏作品(抖音)模式"
msgstr "Exited Batch Download Favorites Works (DouYin) mode."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:2101
msgid "已退出批量下载收藏夹作品(抖音)模式"
msgstr "Exited Batch Download Collections Works (DouYin) mode."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:2126
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\interface\collects.py:27
msgid "收藏夹"
msgstr "Collections"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:2137
#, python-brace-format
msgid "配置文件 owner_url 的 url 参数 {url} 无效"
msgstr ""
"The URL parameter {url} of owner_url in the configuration file is invalid."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:2167
msgid "已退出批量下载收藏音乐(抖音)模式"
msgstr "Exited Batch Download Collections Music (DouYin) mode."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:2175
#, python-brace-format
msgid "程序运行耗时 {minutes} 分钟 {seconds} 秒"
msgstr "The program ran for {minutes} minutes {seconds} seconds"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:2205
msgid "开始获取收藏数据"
msgstr "Starting to retrieve Favorites data"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:2215
#, python-brace-format
msgid "{sec_user_id} 获取账号信息失败"
msgstr "Failed to retrieve account information for {sec_user_id}"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:2248
msgid "开始获取收藏夹数据"
msgstr "Starting to retrieve Collections data"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\main_terminal.py:2301
msgid "请选择采集功能"
msgstr "Please select the function menu"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:108
msgid "禁用"
msgstr "Disable"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:109
msgid "启用"
msgstr "Enable"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:112
msgid "从剪贴板读取 Cookie (抖音)"
msgstr "Extracting cookie (DouYin) from clipboard"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:113
msgid "从浏览器读取 Cookie (抖音)"
msgstr "Extracting cookie (DouYin) from browser"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:115
msgid "从剪贴板读取 Cookie (TikTok)"
msgstr "Extracting cookie (TikTok) from clipboard"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:116
msgid "从浏览器读取 Cookie (TikTok)"
msgstr "Extracting cookie (TikTok) from browser"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:117
msgid "终端交互模式"
msgstr "Terminal Mode"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:118
msgid "后台监听模式"
msgstr "Monitoring Mode"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:119
msgid "Web API 模式"
msgstr "Web API Mode"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:120
msgid "Web UI 模式"
msgstr "Web UI Mode"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:124
msgid "{}作品下载记录"
msgstr "{} Works Download History"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:127
msgid "删除作品下载记录"
msgstr "Delete Works Download History"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:129
msgid "{}运行日志记录"
msgstr "{} Run Log History"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:132
msgid "检查程序版本更新"
msgstr "Check for Program Version Updates"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:133
msgid "切换语言"
msgstr "切换到简体中文"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:149
msgid ""
"访问 http://127.0.0.1:5555/docs 或者 http://127.0.0.1:5555/redoc 可以查阅 "
"API 模式说明文档!"
msgstr ""
"Visit http://127.0.0.1:5555/docs or http://127.0.0.1:5555/redoc to view the "
"API mode documentation!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:190
msgid "是否已仔细阅读上述免责声明(YES/NO): "
msgstr "Have you carefully read the above disclaimer (YES/NO):"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:224
msgid "项目地址: {}"
msgstr "Project URL: {}"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:225
msgid "项目文档: {}"
msgstr "Project Documentation: {}"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:226
msgid "开源许可: {}\n"
msgstr "Open Source License: {}\n"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:248
#, python-brace-format
msgid "检测到新版本: {major}.{minor}"
msgstr "New version detected: {major}.{minor}"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:255
msgid "当前版本为开发版, 可更新至正式版"
msgstr ""
"The current version is a development version, update to the stable version."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:260
msgid "当前已是最新开发版"
msgstr "The current version is the latest development version."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:264
msgid "当前已是最新正式版"
msgstr "The current version is the latest stable version."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:268
msgid "检测新版本失败"
msgstr "Failed to check for new version."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:280
msgid "DouK-Downloader 功能选项"
msgstr "DouK-Downloader Feature Options"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:322
msgid "修改设置成功!"
msgstr "Settings updated successfully!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:334
msgid "Cookie 获取教程:"
msgstr "Cookie retrieval tutorial:"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:340
msgid ""
"复制 Cookie 内容至剪贴板后,按回车键确认继续;若输入任意内容并按回车,则取消"
"操作:"
msgstr ""
"After pasting the cookie into the clipboard, press Enter to proceed. Enter "
"any text + Enter to abort."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:379
msgid "作品下载记录功能已禁用!"
msgstr "Works download history feature is disabled!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\application\TikTokDownloader.py:445
msgid "正在关闭程序"
msgstr "Shutting down the program"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\parameter.py:273
#, python-brace-format
msgid "{name} 参数格式错误"
msgstr "{name} parameter format error"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\parameter.py:366
#, python-brace-format
msgid "root 参数 {root} 不是有效的文件夹路径,程序将使用项目根路径作为储存路径"
msgstr ""
"The root parameter {root} is not a valid folder path. The program will use "
"the project root path as the storage path."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\parameter.py:386
#, python-brace-format
msgid ""
"folder_name 参数 {folder_name} 不是有效的文件夹名称,程序将使用默认值:"
"Download"
msgstr ""
"folder_name parameter {folder_name} is not a valid folder name. The program "
"will use the default value: Download"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\parameter.py:399
#, python-brace-format
msgid ""
"name_format 参数 {name_format} 设置错误,程序将使用默认值:创建时间 作品类型 "
"账号昵称 作品描述"
msgstr ""
"name_format parameter {name_format} is set incorrectly. The program will use "
"the default value: Time, Type, Nickname, Description"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\parameter.py:412
#, python-brace-format
msgid ""
"date_format 参数 {date_format} 设置错误,程序将使用默认值:年-月-日 时:分:秒"
msgstr ""
"date_format parameter {date_format} is set incorrectly. The program will use "
"the default value: Year-Month-Day Hour:Minute:Second"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\parameter.py:421
#, python-brace-format
msgid "split 参数 {split} 包含非法字符,程序将使用默认值:-"
msgstr ""
"split parameter {split} contains illegal characters. The program will use "
"the default value: -"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\parameter.py:451
#, python-brace-format
msgid "{remark}代理参数应为字符串格式,未来不再支持字典格式"
msgstr ""
"{remark} proxy parameter should be in string format. Dictionary format will "
"no longer be supported in the future."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\parameter.py:467
#, python-brace-format
msgid "{remark}代理 {proxy} 测试成功"
msgstr "{remark} proxy {proxy} test successful."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\parameter.py:474
#, python-brace-format
msgid "{remark}代理 {proxy} 测试超时"
msgstr "{remark} proxy {proxy} test timed out."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\parameter.py:484
#, python-brace-format
msgid "{remark}代理 {proxy} 测试失败:{error}"
msgstr "{remark} proxy {proxy} test failed: {error}"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\parameter.py:519
#, python-brace-format
msgid "max_pages 参数 {max_pages} 设置错误,程序将使用默认值:99999"
msgstr ""
"max_pages parameter {max_pages} is set incorrectly. The program will use the "
"default value: 99999"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\parameter.py:543
#, python-brace-format
msgid ""
"storage_format 参数 {storage_format} 设置错误,程序默认不会储存任何数据至文件"
msgstr ""
"The storage_format parameter {storage_format} is set incorrectly. By "
"default, the program will not store any data to files."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\parameter.py:561
msgid "正在更新抖音参数,请稍等..."
msgstr "Updating DouYin parameters, please wait..."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\parameter.py:579
msgid "抖音参数更新完毕!"
msgstr "DouYin parameters update completed!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\parameter.py:583
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\parameter.py:644
msgid "配置文件 cookie 参数未设置,抖音平台功能可能无法正常使用"
msgstr ""
"The cookie parameter is not configured in the settings file. DouYin platform "
"features may not work properly."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\parameter.py:593
msgid "正在更新 TikTok 参数,请稍等..."
msgstr "Updating TikTok parameters, please wait..."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\parameter.py:611
msgid "TikTok 参数更新完毕!"
msgstr "TikTok parameters update completed!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\parameter.py:616
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\parameter.py:667
msgid "配置文件 cookie_tiktok 参数未设置,TikTok 平台功能可能无法正常使用"
msgstr ""
"The cookie_tiktok parameter is not configured in the settings file. TikTok "
"platform features may not work properly."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\parameter.py:772
#, python-brace-format
msgid "TikTok cookie 缺少 {name} 键值对,请尝试重新写入 cookie"
msgstr ""
"The TikTok cookie is missing the {name} key-value pair. Please attempt to "
"rewrite the cookie."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\parameter.py:1112
#, python-brace-format
msgid "{key} 参数 {value} 设置过小,程序将使用默认值:{default}"
msgstr ""
"The parameter {key} has been set to a value ({value}) that is too small. The "
"program will use the default value instead: {default}"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\parameter.py:1120
#, python-brace-format
msgid "{key} 参数 {value} 设置错误,程序将使用默认值:{default}"
msgstr ""
"The parameter {key} is incorrectly configured ({value}). The program will "
"use the default value: {default}."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\parameter.py:1133
#, python-brace-format
msgid "live_qualities 参数 {live_qualities} 设置错误"
msgstr ""
"The live_qualities parameter is incorrectly configured: {live_qualities}"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\settings.py:157
msgid ""
"创建默认配置文件 settings.json 成功!\n"
"请参考项目文档的快速入门部分,设置 Cookie 后重新运行程序!\n"
"建议根据实际使用需求修改配置文件 settings.json!\n"
msgstr ""
"Default configuration file settings.json created successfully!\n"
"Please refer to the quick start section of the project documentation, set "
"the Cookie, and rerun the program!\n"
"It is recommended to modify the settings.json file according to your actual "
"usage needs!\n"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\settings.py:174
msgid "配置文件 settings.json 格式错误,请检查 JSON 格式!"
msgstr ""
"The configuration file settings.json has an error. Please check the JSON "
"format!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\settings.py:186
#, python-brace-format
msgid "配置文件 settings.json 缺少参数 {i},已自动添加该参数!"
msgstr ""
"The configuration file settings.json is missing the parameter \"{i}\". The "
"program has automatically added this parameter."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\settings.py:204
msgid "保存配置成功!"
msgstr "Configuration saved successfully!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\config\settings.py:216
#, python-brace-format
msgid "配置文件 {old} 参数已变更为 {new} 参数,请注意修改配置文件!"
msgstr ""
"The configuration file parameter {old} has been changed to {new}. Please "
"make sure to update the configuration file accordingly!"

msgid ""
"关于 DouK-Downloader 的 免责声明:\n"
"\n"
"1. 使用者对本项目的使用由使用者自行决定,并自行承担风险。作者对使用者使用本项"
"目所产生的任何损失、责任、或风险概不负责。\n"
"2. 本项目的作者提供的代码和功能是基于现有知识和技术的开发成果。作者按现有技术"
"水平努力确保代码的正确性和安全性,但不保证代码完全没有错误或缺陷。\n"
"3. 本项目依赖的所有第三方库、插件或服务各自遵循其原始开源或商业许可,使用者需"
"自行查阅并遵守相应协议,作者不对第三方组件的稳定性、安全性及合规性承担任何责"
"任。\n"
"4. 使用者在使用本项目时必须严格遵守 GNU General Public License v3.0 的要求,"
"并在适当的地方注明使用了 GNU General Public License v3.0 的代码。\n"
"5. 使用者在使用本项目的代码和功能时,必须自行研究相关法律法规,并确保其使用行"
"为合法合规。任何因违反法律法规而导致的法律责任和风险,均由使用者自行承担。\n"
"6. 使用者不得使用本工具从事任何侵犯知识产权的行为,包括但不限于未经授权下载、"
"传播受版权保护的内容,开发者不参与、不支持、不认可任何非法内容的获取或分"
"发。\n"
"7. 本项目不对使用者涉及的数据收集、存储、传输等处理活动的合规性承担责任。使用"
"者应自行遵守相关法律法规,确保处理行为合法正当;因违规操作导致的法律责任由使"
"用者自行承担。\n"
"8. 使用者在任何情况下均不得将本项目的作者、贡献者或其他相关方与使用者的使用行"
"为联系起来,或要求其对使用者使用本项目所产生的任何损失或损害负责。\n"
"9. 本项目的作者不会提供 DouK-Downloader 项目的付费版本,也不会提供与 DouK-"
"Downloader 项目相关的任何商业服务。\n"
"10. 基于本项目进行的任何二次开发、修改或编译的程序与原创作者无关,原创作者不"
"承担与二次开发行为或其结果相关的任何责任,使用者应自行对因二次开发可能带来的"
"各种情况负全部责任。\n"
"11. 本项目不授予使用者任何专利许可;若使用本项目导致专利纠纷或侵权,使用者自"
"行承担全部风险和责任。未经作者或权利人书面授权,不得使用本项目进行任何商业宣"
"传、推广或再授权。\n"
"12. 作者保留随时终止向任何违反本声明的使用者提供服务的权利,并可能要求其销毁"
"已获取的代码及衍生作品。\n"
"13. 作者保留在不另行通知的情况下更新本声明的权利,使用者持续使用即视为接受修"
"订后的条款。\n"
"\n"
"在使用本项目的代码和功能之前,请您认真考虑并接受以上免责声明。如果您对上述声"
"明有任何疑问或不同意,请不要使用本项目的代码和功能。如果您使用了本项目的代码"
"和功能,则视为您已完全理解并接受上述免责声明,并自愿承担使用本项目的一切风险"
"和后果。\n"
msgstr ""
"Disclaimer for DouK-Downloader:\n"
"\n"
"1. The use of this project is entirely at the user's own discretion and "
"risk. The author assumes no responsibility or liability of any kind for any "
"loss, damage, or risk arising from the user's use of this project.\n"
"2. The code and functionalities provided by the author of this project are "
"developed based on current knowledge and technology. The author makes every "
"effort to ensure the correctness and security of the code according to "
"current technical standards but does not guarantee that the code is "
"completely free of errors or defects.\n"
"3. All third-party libraries, plugins, or services used by this project "
"follow their original open-source or commercial licenses. Users must review "
"and comply with these license agreements accordingly. The author assumes no "
"responsibility for the stability, security, or compliance of any third-party "
"components.\n"
"4. When using this project, users must strictly comply with the requirements "
"of the GNU General Public License v3.0 and clearly indicate in appropriate "
"places that the code was used under the GNU General Public License v3.0.\n"
"5. When using the code and functionalities of this project, users must "
"independently research relevant laws and regulations and ensure that their "
"usage is legal and compliant. Any legal liabilities or risks arising from "
"violations of laws and regulations shall be borne solely by the user.\n"
"6. Users must not use this tool to engage in any activities that infringe "
"intellectual property rights, including but not limited to downloading or "
"distributing copyrighted content without authorization. Developers do not "
"participate in, support, or endorse the acquisition or distribution of any "
"illegal or unauthorized content.\n"
"7. This project assumes no responsibility for the compliance of data "
"processing activities (including collection, storage, and transmission) "
"performed by users. Users must comply with relevant laws and regulations and "
"ensure that such activities are lawful and proper. Legal liabilities "
"resulting from non-compliant operations shall be borne by the user.\n"
"8. Under no circumstances may users associate the author, contributors, or "
"other related parties of this project with their usage of the project, nor "
"may they hold these parties liable for any loss or damage resulting from "
"such usage.\n"
"9. The author of this project will not provide a paid version of the DouK-"
"Downloader project, nor will they offer any commercial services related to "
"it.\n"
"10. Any secondary development, modification, or compilation based on this "
"project is unrelated to the original author. The original author assumes no "
"liability for any consequences resulting from such secondary development. "
"Users bear full responsibility for all outcomes arising from such "
"modifications.\n"
"11. This project does not grant users any patent licenses. If the use of "
"this project leads to patent disputes or infringement, the user assumes all "
"associated risks and responsibilities. Without written authorization from "
"the author or rights holder, users may not use this project for any "
"commercial promotion, advertising, or re-licensing.\n"
"12. The author reserves the right to terminate service to any user who "
"violates this disclaimer at any time and may require them to destroy all "
"obtained code and derivative works.\n"
"13. The author reserves the right to update this disclaimer at any time "
"without prior notice. Continued use of the project constitutes acceptance of "
"the revised terms.\n"
"\n"
"Before using the code and functionalities of this project, please carefully "
"consider and accept the above disclaimer. If you have any questions or "
"disagree with the above statements, please do not use the code and "
"functionalities of this project. If you do use the code and functionalities "
"of this project, it shall be deemed that you have fully understood and "
"accepted the above disclaimer and voluntarily assume all risks and "
"consequences associated with its use.\n"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\custom\function.py:56
#, python-brace-format
msgid ""
"程序连续处理了 {batches} 个数据,为了避免请求频率过高导致账号或 IP 被风控,程"
"序已经暂停运行,将在 {rest_time} 秒后恢复运行!"
msgstr ""
"The program has continuously processed {batches} items. To avoid high "
"request frequency that could lead to account or IP being restricted, the "
"program has paused and will resume in {rest_time} seconds!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\downloader\download.py:159
msgid "开始下载作品文件"
msgstr "Start downloading the works file."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\downloader\download.py:235
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\downloader\download.py:343
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\downloader\download.py:501
msgid "音乐"
msgstr "Music"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\downloader\download.py:255
msgid "程序将会调用 ffmpeg 下载直播,关闭 DouK-Downloader 不会中断下载!"
msgstr ""
"The program will call ffmpeg to download the live stream. Closing DouK-"
"Downloader will not interrupt the download!"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\downloader\download.py:332
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\downloader\download.py:335
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\downloader\download.py:753
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\extract\extractor.py:422
msgid "实况"
msgstr "LivePhoto"

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\downloader\download.py:409
#: C:\Users\You\PycharmProjects\TikTokDownloader\src\downloader\download.py:460
#, python-brace-format
msgid "【{type}】{name} 提取文件下载地址失败,跳过下载"
msgstr ""
"【{type}】{name} failed to retrieve the file download URL, skipping download."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\downloader\download.py:421
#, python-brace-format
msgid "【{type}】{name} 存在下载记录,跳过下载"
msgstr "【{type}】{name} has a download record, skipping download."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\downloader\download.py:428
#, python-brace-format
msgid "【{type}】{name}_{index} 文件已存在,跳过下载"
msgstr "【{type}】{name}_{index} file already exists, skipping download."

#: C:\Users\You\PycharmProjects\TikTokDownloader\src\downloader\download.py:472
#, python-brace-format
msgid "【{type}】{name} 存在下载记录或文件已存在,跳过下载"
msgstr ""
"【{type}】{name} has a download record or file already exists, skipping "
"download."

#: C:\Users\You\PycharmProjects\TikTokDow
Download .txt
gitextract_3f4xxgla/

├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── dependabot.yml
│   └── workflows/
│       ├── Close_Stale_Issues_and_PRs.yaml
│       ├── Delete_untagged_images.yml
│       ├── Manually_build_executable_programs.yml
│       ├── Manually_docker_image.yml
│       ├── Release_build_executable_program.yml
│       └── Release_docker_image.yml
├── .gitignore
├── .python-version
├── Dockerfile
├── README.md
├── README_EN.md
├── docs/
│   ├── Cookie获取教程.md
│   ├── DouK-Downloader文档.md
│   └── Release_Notes.md
├── license
├── locale/
│   ├── README.md
│   ├── en_US/
│   │   └── LC_MESSAGES/
│   │       ├── tk.mo
│   │       └── tk.po
│   ├── generate_path.py
│   ├── po_to_mo.py
│   ├── tk.pot
│   └── zh_CN/
│       └── LC_MESSAGES/
│           ├── tk.mo
│           └── tk.po
├── main.py
├── pyproject.toml
├── requirements.txt
├── src/
│   ├── application/
│   │   ├── TikTokDownloader.py
│   │   ├── __init__.py
│   │   ├── main_monitor.py
│   │   ├── main_server.py
│   │   └── main_terminal.py
│   ├── cli_edition/
│   │   ├── __init__.py
│   │   ├── main_cli.py
│   │   └── write.py
│   ├── config/
│   │   ├── __init__.py
│   │   ├── parameter.py
│   │   └── settings.py
│   ├── custom/
│   │   ├── __init__.py
│   │   ├── function.py
│   │   ├── internal.py
│   │   └── static.py
│   ├── downloader/
│   │   ├── __init__.py
│   │   └── download.py
│   ├── encrypt/
│   │   ├── __init__.py
│   │   ├── aBogus.py
│   │   ├── device_id.py
│   │   ├── msToken.py
│   │   ├── ttWid.py
│   │   ├── verifyFp.py
│   │   ├── webID.py
│   │   ├── xBogus.py
│   │   └── xGnarly.py
│   ├── extract/
│   │   ├── __init__.py
│   │   └── extractor.py
│   ├── gui_edition/
│   │   └── __init__.py
│   ├── interface/
│   │   ├── __init__.py
│   │   ├── account.py
│   │   ├── account_tiktok.py
│   │   ├── collection.py
│   │   ├── collects.py
│   │   ├── comment.py
│   │   ├── comment_tiktok.py
│   │   ├── detail.py
│   │   ├── detail_tiktok.py
│   │   ├── hashtag.py
│   │   ├── hot.py
│   │   ├── info.py
│   │   ├── info_tiktok.py
│   │   ├── live.py
│   │   ├── live_tiktok.py
│   │   ├── mix.py
│   │   ├── mix_tiktok.py
│   │   ├── search.py
│   │   ├── slides.py
│   │   ├── template.py
│   │   └── user.py
│   ├── link/
│   │   ├── __init__.py
│   │   ├── extractor.py
│   │   └── requester.py
│   ├── manager/
│   │   ├── __init__.py
│   │   ├── cache.py
│   │   ├── database.py
│   │   └── recorder.py
│   ├── models/
│   │   ├── __init__.py
│   │   ├── account.py
│   │   ├── base.py
│   │   ├── comment.py
│   │   ├── detail.py
│   │   ├── live.py
│   │   ├── mix.py
│   │   ├── reply.py
│   │   ├── response.py
│   │   ├── search.py
│   │   ├── settings.py
│   │   └── share.py
│   ├── module/
│   │   ├── __init__.py
│   │   ├── cookie.py
│   │   ├── ffmpeg.py
│   │   ├── migrate_folder.py
│   │   ├── register.py
│   │   ├── tiktok_account_index.py
│   │   └── tiktok_unofficial.py
│   ├── record/
│   │   ├── __init__.py
│   │   ├── base.py
│   │   └── logger.py
│   ├── storage/
│   │   ├── __init__.py
│   │   ├── csv.py
│   │   ├── manager.py
│   │   ├── mysql.py
│   │   ├── sql.py
│   │   ├── sqlite.py
│   │   ├── text.py
│   │   └── xlsx.py
│   ├── testers/
│   │   ├── __init__.py
│   │   ├── logger.py
│   │   ├── params.py
│   │   ├── test_format.py
│   │   └── translate.py
│   ├── tools/
│   │   ├── __init__.py
│   │   ├── browser.py
│   │   ├── capture.py
│   │   ├── choose.py
│   │   ├── cleaner.py
│   │   ├── console.py
│   │   ├── error.py
│   │   ├── file_folder.py
│   │   ├── format.py
│   │   ├── list_pop.py
│   │   ├── progress.py
│   │   ├── rename_compatible.py
│   │   ├── retry.py
│   │   ├── session.py
│   │   ├── temporary.py
│   │   ├── timer.py
│   │   └── truncate.py
│   ├── translation/
│   │   ├── __init__.py
│   │   ├── static.py
│   │   └── translate.py
│   └── tui_edition/
│       ├── __init__.py
│       ├── app.py
│       └── setting.py
└── static/
    ├── images/
    │   └── DouK-Downloader.icns
    └── js/
        ├── X-Bogus.js
        └── a_bogus.js
Download .txt
SYMBOL INDEX (1012 symbols across 97 files)

FILE: locale/generate_path.py
  function find_python_files (line 6) | def find_python_files(dir_, file):

FILE: locale/po_to_mo.py
  function scan_directory (line 7) | def scan_directory():
  function generate_map (line 13) | def generate_map(files: list[Path]):
  function generate_mo (line 17) | def generate_mo(maps: list[tuple[Path, Path]]):

FILE: main.py
  function main (line 7) | async def main():

FILE: src/application/TikTokDownloader.py
  class TikTokDownloader (line 49) | class TikTokDownloader:
    method __init__ (line 57) | def __init__(
    method rename_compatible (line 79) | def rename_compatible():
    method read_config (line 82) | async def read_config(self):
    method __format_config (line 88) | def __format_config(config: list) -> dict:
    method set_language (line 92) | def set_language(language: str) -> None:
    method __aenter__ (line 95) | async def __aenter__(self):
    method __aexit__ (line 100) | async def __aexit__(self, exc_type, exc_val, exc_tb):
    method __update_menu (line 106) | def __update_menu(self):
    method disable_function (line 136) | async def disable_function(
    method server (line 145) | async def server(self):
    method __modify_record (line 163) | async def __modify_record(self):
    method __modify_logging (line 166) | async def __modify_logging(self):
    method _switch_language (line 169) | async def _switch_language(
    method _update_language (line 180) | async def _update_language(self, language: str) -> None:
    method disclaimer (line 185) | async def disclaimer(self):
    method __init_language (line 197) | async def __init_language(self):
    method project_info (line 219) | def project_info(self):
    method check_config (line 228) | def check_config(self):
    method check_update (line 236) | async def check_update(self):
    method main_menu (line 271) | async def main_menu(
    method complete (line 291) | async def complete(self):
    method monitor (line 303) | async def monitor(self):
    method monitor_clipboard (line 306) | async def monitor_clipboard(self):
    method change_config (line 316) | async def change_config(
    method write_cookie (line 326) | async def write_cookie(self):
    method write_cookie_tiktok (line 329) | async def write_cookie_tiktok(self):
    method __write_cookie (line 332) | async def __write_cookie(self, tiktok: bool):
    method compatible (line 366) | async def compatible(self, mode: str):
    method delete_works_ids (line 376) | async def delete_works_ids(self):
    method check_settings (line 387) | async def check_settings(self, restart=True):
    method run (line 408) | async def run(self):
    method periodic_update_params (line 417) | def periodic_update_params(self):
    method restart_cycle_task (line 427) | def restart_cycle_task(
    method close (line 440) | def close(self):
    method browser_cookie (line 447) | async def browser_cookie(
    method browser_cookie_tiktok (line 455) | async def browser_cookie_tiktok(

FILE: src/application/main_monitor.py
  class ClipboardMonitor (line 15) | class ClipboardMonitor(TikTok):
    method __init__ (line 16) | def __init__(
    method run (line 32) | async def run(self, run_command: list):
    method start_listener (line 35) | async def start_listener(
    method stop_listener (line 58) | async def stop_listener(self):
    method check_clipboard (line 62) | async def check_clipboard(
    method check_link (line 75) | async def check_link(
    method deal_tasks (line 88) | async def deal_tasks(
    method deal_tasks_tiktok (line 100) | async def deal_tasks_tiktok(
    method _deal_tasks (line 112) | async def _deal_tasks(
  class PostMonitor (line 139) | class PostMonitor(TikTok):
    method __init__ (line 140) | def __init__(

FILE: src/application/main_server.py
  function token_dependency (line 46) | def token_dependency(token: str = Header(None)):
  class APIServer (line 54) | class APIServer(TikTok):
    method __init__ (line 55) | def __init__(
    method handle_redirect (line 68) | async def handle_redirect(self, text: str, proxy: str = None) -> str:
    method handle_redirect_tiktok (line 75) | async def handle_redirect_tiktok(self, text: str, proxy: str = None) -...
    method run_server (line 82) | async def run_server(
    method setup_routes (line 103) | def setup_routes(self):
    method handle_search (line 646) | async def handle_search(self, extract):
    method handle_detail (line 660) | async def handle_detail(
    method handle_account (line 679) | async def handle_account(
    method success_response (line 703) | def success_response(
    method failed_response (line 715) | def failed_response(
    method generate_mix_params (line 726) | def generate_mix_params(mix_id: str = None, detail_id: str = None):
    method check_live_params (line 732) | def check_live_params(
    method handle_live (line 739) | async def handle_live(self, extract: Live | LiveTikTok, tiktok=False):

FILE: src/application/main_terminal.py
  function check_storage_format (line 65) | def check_storage_format(function):
  function check_cookie_state (line 78) | def check_cookie_state(tiktok=False):
  class TikTok (line 100) | class TikTok:
    method __init__ (line 103) | def __init__(
    method _inquire_input (line 278) | def _inquire_input(
    method account_acquisition_interactive_tiktok (line 291) | async def account_acquisition_interactive_tiktok(
    method __summarize_results (line 302) | def __summarize_results(self, count: SimpleNamespace, name=_("账号")):
    method account_acquisition_interactive (line 317) | async def account_acquisition_interactive(
    method __secondary_menu (line 328) | async def __secondary_menu(
    method account_detail_batch (line 354) | async def account_detail_batch(
    method account_detail_batch_tiktok (line 364) | async def account_detail_batch_tiktok(
    method __account_detail_batch (line 374) | async def __account_detail_batch(
    method check_sec_user_id (line 418) | async def check_sec_user_id(
    method account_detail_inquire (line 430) | async def account_detail_inquire(
    method account_detail_inquire_tiktok (line 447) | async def account_detail_inquire_tiktok(
    method account_detail_txt (line 464) | async def account_detail_txt(
    method _read_from_txt (line 474) | async def _read_from_txt(
    method account_detail_txt_tiktok (line 502) | async def account_detail_txt_tiktok(
    method __account_detail_handle (line 512) | async def __account_detail_handle(
    method deal_account_detail (line 538) | async def deal_account_detail(
    method _get_account_data (line 617) | async def _get_account_data(
    method _get_account_data_tiktok (line 640) | async def _get_account_data_tiktok(
    method get_user_info_data (line 663) | async def get_user_info_data(
    method _get_info_data (line 686) | async def _get_info_data(
    method _get_info_data_tiktok (line 699) | async def _get_info_data_tiktok(
    method _batch_process_detail (line 714) | async def _batch_process_detail(
    method _generate_prefix (line 806) | def _generate_prefix(
    method _generate_suffix (line 820) | def _generate_suffix(
    method __display_extracted_information (line 837) | def __display_extracted_information(
    method download_detail_batch (line 851) | async def download_detail_batch(
    method detail_interactive (line 879) | async def detail_interactive(
    method detail_interactive_tiktok (line 890) | async def detail_interactive_tiktok(
    method detail_interactive_tiktok_unofficial (line 900) | async def detail_interactive_tiktok_unofficial(
    method __detail_secondary_menu (line 913) | async def __detail_secondary_menu(self, menu, select="", *args, **kwar...
    method __detail_inquire (line 931) | async def __detail_inquire(
    method __detail_inquire_tiktok (line 952) | async def __detail_inquire_tiktok(
    method __detail_inquire_tiktok_unofficial (line 960) | async def __detail_inquire_tiktok_unofficial(
    method __detail_txt (line 975) | async def __detail_txt(
    method __detail_txt_tiktok (line 989) | async def __detail_txt_tiktok(
    method __detail_txt_tiktok_unofficial (line 997) | async def __detail_txt_tiktok_unofficial(
    method __read_detail_txt (line 1009) | async def __read_detail_txt(self):
    method _handle_detail (line 1021) | async def _handle_detail(
    method handle_detail_single (line 1043) | async def handle_detail_single(
    method __handle_detail (line 1057) | async def __handle_detail(
    method _get_preview_image (line 1092) | def _get_preview_image(data: dict) -> str:
    method _choice_live_quality (line 1099) | def _choice_live_quality(
    method get_live_data (line 1120) | async def get_live_data(
    method get_live_data_tiktok (line 1137) | async def get_live_data_tiktok(
    method live_interactive (line 1145) | async def live_interactive(
    method live_interactive_tiktok (line 1160) | async def live_interactive_tiktok(
    method show_live_info (line 1194) | def show_live_info(self, data: list[dict]) -> list[tuple]:
    method show_live_info_tiktok (line 1207) | def show_live_info_tiktok(self, data: list[dict]) -> list[tuple]:
    method show_live_stream_url (line 1222) | def show_live_stream_url(self, item: dict, tasks: list):
    method show_live_stream_url_tiktok (line 1241) | def show_live_stream_url_tiktok(self, item: dict, tasks: list):
    method comment_interactive_tiktok (line 1262) | async def comment_interactive_tiktok(self, select="", *args, **kwargs):
    method comment_interactive (line 1267) | async def comment_interactive(
    method __comment_inquire (line 1278) | async def __comment_inquire(
    method __comment_inquire_tiktok (line 1298) | async def __comment_inquire_tiktok(
    method __comment_txt (line 1305) | async def __comment_txt(
    method comment_handle_single (line 1316) | async def comment_handle_single(
    method comment_handle_single_tiktok (line 1334) | async def comment_handle_single_tiktok(
    method comment_handle (line 1343) | async def comment_handle(
    method save_comment (line 1370) | async def save_comment(self, detail_id: str, data: list[dict]) -> list:
    method reply_handle (line 1382) | async def reply_handle(
    method reply_handle_tiktok (line 1406) | async def reply_handle_tiktok(
    method mix_interactive (line 1418) | async def mix_interactive(
    method mix_interactive_tiktok (line 1429) | async def mix_interactive_tiktok(
    method _generate_mix_params (line 1441) | def _generate_mix_params(mix: bool, id_: str) -> dict:
    method mix_inquire (line 1452) | async def mix_inquire(
    method mix_inquire_tiktok (line 1467) | async def mix_inquire_tiktok(
    method mix_collection (line 1483) | async def mix_collection(
    method mix_inquire_collection (line 1492) | async def mix_inquire_collection(self) -> list[str]:
    method input_download_index (line 1499) | def input_download_index(self, data: list[dict]) -> list[str] | None:
    method __input_download_index (line 1506) | def __input_download_index(
    method mix_txt (line 1534) | async def mix_txt(
    method mix_txt_tiktok (line 1544) | async def mix_txt_tiktok(
    method __mix_handle (line 1567) | async def __mix_handle(
    method mix_batch (line 1593) | async def mix_batch(
    method mix_batch_tiktok (line 1602) | async def mix_batch_tiktok(
    method __mix_batch (line 1611) | async def __mix_batch(
    method deal_mix_detail (line 1653) | async def deal_mix_detail(
    method _check_mix_id (line 1706) | async def _check_mix_id(
    method user_batch (line 1721) | async def user_batch(
    method user_inquire (line 1738) | async def user_inquire(
    method txt_inquire (line 1753) | def txt_inquire(self) -> str:
    method user_txt (line 1767) | async def user_txt(
    method _get_user_data (line 1781) | async def _get_user_data(
    method _deal_user_data (line 1798) | async def _deal_user_data(
    method user_interactive (line 1819) | async def user_interactive(self, select="", *args, **kwargs):
    method _enter_search_criteria (line 1827) | def _enter_search_criteria(
    method fill_search_criteria (line 1842) | def fill_search_criteria(criteria: list[Any]) -> list[Any]:
    method search_interactive (line 1850) | async def search_interactive(
    method generate_model (line 1862) | def generate_model(
    method _search_interactive_general (line 1912) | async def _search_interactive_general(
    method _search_interactive_video (line 1936) | async def _search_interactive_video(self):
    method _search_interactive_user (line 1941) | async def _search_interactive_user(self):
    method _search_interactive_live (line 1946) | async def _search_interactive_live(self):
    method _generate_search_name (line 1952) | def _generate_search_name(
    method deal_search_data (line 1996) | async def deal_search_data(
    method hot_interactive (line 2027) | async def hot_interactive(
    method _deal_hot_data (line 2034) | async def _deal_hot_data(
    method collection_interactive (line 2071) | async def collection_interactive(
    method collects_interactive (line 2084) | async def collects_interactive(
    method __get_collects_list (line 2103) | async def __get_collects_list(
    method __check_owner_url (line 2131) | async def __check_owner_url(
    method collection_music_interactive (line 2149) | async def collection_music_interactive(
    method _time_statistics (line 2169) | def _time_statistics(
    method __handle_collection_music (line 2180) | async def __handle_collection_music(
    method _deal_collection_data (line 2196) | async def _deal_collection_data(
    method _deal_collects_data (line 2238) | async def _deal_collects_data(
    method hashtag_interactive (line 2268) | async def hashtag_interactive(
    method handle_detail_unofficial (line 2281) | async def handle_detail_unofficial(
    method run (line 2296) | async def run(self, run_command: list):

FILE: src/cli_edition/main_cli.py
  class Cli (line 4) | class Cli:
  function cli (line 8) | def cli():

FILE: src/cli_edition/write.py
  class Write (line 10) | class Write:
    method __init__ (line 11) | def __init__(
    method run (line 20) | def run(self):
    method generate_data (line 25) | def generate_data(self, data: str):
    method txt_inquire (line 39) | def txt_inquire(self) -> str:

FILE: src/config/parameter.py
  class Parameter (line 48) | class Parameter:
    method __init__ (line 65) | def __init__(
    method check_bool_false (line 255) | def check_bool_false(
    method check_bool_true (line 261) | def check_bool_true(
    method __check_cookie_tiktok (line 266) | def __check_cookie_tiktok(
    method __check_cookie (line 276) | def __check_cookie(self, cookie: dict | str, name="cookie") -> tuple[d...
    method __get_cookie (line 285) | def __get_cookie(
    method __get_cookie_cache (line 291) | def __get_cookie_cache(
    method __get_cookie_tiktok (line 297) | def __get_cookie_tiktok(
    method __get_cookie_tiktok_cache (line 303) | def __get_cookie_tiktok_cache(
    method __add_cookie (line 309) | def __add_cookie(
    method __get_tt_wid_params (line 334) | async def __get_tt_wid_params(self) -> dict:
    method __get_tt_wid_params_tiktok (line 344) | async def __get_tt_wid_params_tiktok(self) -> dict:
    method __check_root (line 364) | def __check_root(self, root: str) -> Path:
    method __check_root_again (line 381) | def __check_root_again(root: Path) -> bool | Path:
    method __check_folder_name (line 387) | def __check_folder_name(self, folder_name: str) -> str:
    method __check_name_format (line 400) | def __check_name_format(self, name_format: str) -> list[str]:
    method __check_date_format (line 413) | def __check_date_format(self, date_format: str) -> str:
    method __check_split (line 426) | def __check_split(self, split: str) -> str:
    method __check_proxy_tiktok (line 438) | def __check_proxy_tiktok(
    method __check_proxy (line 449) | def __check_proxy(
    method __check_max_size (line 500) | def __check_max_size(self, max_size: int) -> int:
    method __check_chunk (line 505) | def __check_chunk(self, chunk: int) -> int:
    method __check_max_retry (line 513) | def __check_max_retry(self, max_retry: int) -> int:
    method __check_max_pages (line 521) | def __check_max_pages(self, max_pages: int) -> int:
    method __check_timeout (line 533) | def __check_timeout(self, timeout: int | float) -> int | float:
    method __check_storage_format (line 541) | def __check_storage_format(self, storage_format: str) -> str:
    method __check_run_command (line 558) | def __check_run_command(run_command: str) -> list:
    method update_params (line 561) | async def update_params(self) -> None:
    method update_params_offline (line 629) | async def update_params_offline(self) -> None:
    method __update_cookie (line 680) | async def __update_cookie(
    method set_headers_cookie (line 696) | def set_headers_cookie(
    method set_download_headers (line 714) | def set_download_headers(self) -> None:
    method __update_download_headers (line 718) | def __update_download_headers(self) -> None:
    method __update_download_headers_tiktok (line 721) | def __update_download_headers_tiktok(self) -> None:
    method __get_token_params (line 732) | async def __get_token_params(self) -> dict:
    method __get_token_params_tiktok (line 770) | async def __get_token_params_tiktok(self) -> dict:
    method set_uif_id (line 805) | def set_uif_id(
    method __generate_ffmpeg_object (line 817) | def __generate_ffmpeg_object(ffmpeg_path: str) -> FFMPEG:
    method get_settings_data (line 820) | def get_settings_data(self) -> dict:
    method set_settings_data (line 855) | async def set_settings_data(
    method __update_cookie_data (line 893) | async def __update_cookie_data(self, data: dict) -> None:
    method check_urls_params (line 902) | def check_urls_params(data: list[dict]) -> list[SimpleNamespace]:
    method check_url_params (line 913) | def check_url_params(data: dict) -> SimpleNamespace:
    method set_urls_params (line 923) | def set_urls_params(
    method set_cookie (line 945) | def set_cookie(
    method set_general_params (line 961) | def set_general_params(self, data: dict[str, Any]) -> None:
    method set_proxy (line 966) | async def set_proxy(self, proxy: str | None, proxy_tiktok: str | None):
    method merge_browser_info (line 986) | def merge_browser_info(
    method set_browser_info (line 992) | def set_browser_info(self, browser_info: dict, browser_info_tiktok: di...
    method check_str (line 1005) | def check_str(value: str) -> str:
    method close_client (line 1008) | async def close_client(self) -> None:
    method __generate_folders (line 1012) | def __generate_folders(self):
    method __set_browser_info (line 1016) | def __set_browser_info(
    method __set_browser_info_tiktok (line 1056) | def __set_browser_info_tiktok(
    method __check_truncate (line 1089) | def __check_truncate(self, truncate: int) -> int:
    method __check_name_length (line 1097) | def __check_name_length(self, name_length: int) -> int:
    method __check_desc_length (line 1105) | def __check_desc_length(self, desc_length: int) -> int:
    method __check_number_value (line 1113) | def __check_number_value(
    method __check_live_qualities (line 1137) | def __check_live_qualities(self, live_qualities: str) -> str:
    method __check_cookie_state (line 1148) | def __check_cookie_state(self, tiktok=False) -> bool:
    method get_cookie_value (line 1158) | def get_cookie_value(cookie_str: str, key: str, return_key=False) -> str:
    method compatible (line 1185) | def compatible(self):

FILE: src/config/settings.py
  class Settings (line 19) | class Settings:
    method __init__ (line 145) | def __init__(self, root: "Path", console: "ColorfulConsole"):
    method __create (line 151) | def __create(self) -> dict:
    method read (line 164) | def read(self) -> dict:
    method __check (line 178) | def __check(self, data: dict) -> dict:
    method update (line 194) | def update(self, settings: dict | SimpleNamespace):
    method __compatible_with_old_settings (line 207) | def __compatible_with_old_settings(
    method compatible (line 228) | def compatible(self):

FILE: src/custom/function.py
  function wait (line 10) | async def wait() -> None:
  function failure_handling (line 22) | def failure_handling() -> bool:
  function condition_filter (line 32) | def condition_filter(data: dict) -> bool:
  function suspend (line 42) | async def suspend(count: int, console: "ColorfulConsole") -> None:
  function is_valid_token (line 65) | def is_valid_token(token: str) -> bool:

FILE: src/downloader/download.py
  class Downloader (line 44) | class Downloader:
    method __init__ (line 56) | def __init__(
    method init_general_progress (line 96) | def init_general_progress(
    method __fake_progress_object (line 105) | def __fake_progress_object(
    method __general_progress_object (line 111) | def __general_progress_object(self):
    method __live_progress_object (line 131) | def __live_progress_object(self):
    method run (line 150) | async def run(
    method run_batch (line 172) | async def run_batch(
    method run_general (line 204) | async def run_general(self, data: list[dict], tiktok: bool, **kwargs):
    method run_music (line 212) | async def run_music(
    method run_live (line 241) | async def run_live(
    method generate_live_commands (line 259) | def generate_live_commands(
    method __download_live (line 280) | def __download_live(
    method batch_processing (line 291) | async def batch_processing(self, data: list[dict], root: Path, **kwargs):
    method downloader_chart (line 351) | async def downloader_chart(
    method deal_folder_path (line 372) | def deal_folder_path(
    method is_downloaded (line 385) | async def is_downloaded(self, id_: str) -> bool:
    method is_exists (line 389) | def is_exists(path: Path) -> bool:
    method is_skip (line 392) | async def is_skip(self, id_: str, path: Path) -> bool:
    method download_image (line 395) | async def download_image(
    method download_video (line 446) | async def download_video(
    method download_music (line 490) | def download_music(
    method download_cover (line 523) | def download_cover(
    method check_deal_music (line 574) | def check_deal_music(
    method request_file (line 584) | async def request_file(
    method download_file (line 686) | async def download_file(
    method __record_request_messages (line 727) | def __record_request_messages(
    method __adapter_headers (line 738) | def __adapter_headers(
    method add_count (line 748) | def add_count(show: str, id_: str, count: SimpleNamespace):
    method data_classification (line 757) | def data_classification(
    method storage_folder (line 777) | def storage_folder(
    method generate_detail_name (line 802) | def generate_detail_name(self, data: dict) -> str:
    method generate_music_name (line 812) | def generate_music_name(self, data: dict) -> str:
    method create_detail_folder (line 830) | def create_detail_folder(
    method delete (line 838) | def delete(
    method save_file (line 845) | def save_file(cache: Path, actual: Path):
    method delete_file (line 848) | def delete_file(self, path: Path):
    method statistics_count (line 852) | def statistics_count(self, count: SimpleNamespace):
    method _record_response (line 884) | def _record_response(
    method __head_file (line 898) | async def __head_file(
    method _extract_content (line 917) | def _extract_content(
    method __get_resume_byte_position (line 935) | def __get_resume_byte_position(file: Path) -> int:
    method __update_headers_range (line 938) | def __update_headers_range(
    method __extract_type (line 951) | def __extract_type(self, content: str) -> str:
    method __unknown_type (line 956) | def __unknown_type(self, content: str) -> str:
    method _download_initial_check (line 960) | def _download_initial_check(

FILE: src/encrypt/aBogus.py
  class ABogus (line 15) | class ABogus:
    method __init__ (line 40) | def __init__(
    method list_1 (line 56) | def list_1(
    method list_2 (line 74) | def list_2(
    method list_3 (line 91) | def list_3(
    method random_list (line 108) | def random_list(
    method from_char_code (line 134) | def from_char_code(*args):
    method generate_string_1 (line 138) | def generate_string_1(
    method generate_string_2 (line 150) | def generate_string_2(
    method generate_ua_code (line 168) | def generate_ua_code(self, user_agent: str) -> list[int]:
    method generate_string_2_list (line 173) | def generate_string_2_list(
    method reg_to_array (line 205) | def reg_to_array(a):
    method compress (line 219) | def compress(self, a):
    method generate_f (line 248) | def generate_f(cls, e):
    method pad_array (line 271) | def pad_array(arr, length=60):
    method fill (line 276) | def fill(self, length=60):
    method list_4 (line 284) | def list_4(
    method end_check_num (line 351) | def end_check_num(a: list):
    method decode_string (line 358) | def decode_string(
    method replace_func (line 366) | def replace_func(match):
    method de (line 370) | def de(e, r):
    method pe (line 375) | def pe(e):
    method he (line 379) | def he(e, r, t, n):
    method ve (line 387) | def ve(e, r, t, n):
    method convert_to_char_code (line 395) | def convert_to_char_code(a):
    method split_array (line 402) | def split_array(arr, chunk_size=64):
    method char_code_at (line 409) | def char_code_at(s):
    method write (line 412) | def write(
    method reset (line 428) | def reset(
    method sum (line 435) | def sum(self, e, length=60):
    method generate_result_unit (line 443) | def generate_result_unit(cls, n, s):
    method generate_result_end (line 450) | def generate_result_end(cls, s, e="s4"):
    method generate_result (line 459) | def generate_result(cls, s, e="s4"):
    method generate_args_code (line 488) | def generate_args_code(cls):
    method generate_method_code (line 500) | def generate_method_code(self, method: str = "GET") -> list[int]:
    method generate_params_code (line 504) | def generate_params_code(self, params: str) -> list[int]:
    method sm3_to_array (line 509) | def sm3_to_array(cls, data: str | list) -> list[int]:
    method generate_browser_info (line 535) | def generate_browser_info(cls, platform: str = "Win32") -> str:
    method rc4_encrypt (line 564) | def rc4_encrypt(plaintext, key):
    method get_value (line 585) | def get_value(

FILE: src/encrypt/device_id.py
  class DeviceId (line 13) | class DeviceId:
    method get_device_id (line 19) | async def get_device_id(
    method get_device_ids (line 41) | async def get_device_ids(
  function test (line 58) | async def test():

FILE: src/encrypt/msToken.py
  class MsToken (line 22) | class MsToken:
    method get_fake_ms_token (line 81) | def get_fake_ms_token(key="msToken", size=156) -> dict:
    method _get_ms_token (line 90) | async def _get_ms_token(
    method get_real_ms_token (line 111) | async def get_real_ms_token(
    method get_long_ms_token (line 129) | async def get_long_ms_token(
  class MsTokenTikTok (line 146) | class MsTokenTikTok(MsToken):
    method get_real_ms_token (line 210) | async def get_real_ms_token(
  function test (line 236) | async def test():

FILE: src/encrypt/ttWid.py
  class TtWid (line 17) | class TtWid:
    method get_tt_wid (line 26) | async def get_tt_wid(
    method extract (line 45) | def extract(
  class TtWidTikTok (line 56) | class TtWidTikTok(TtWid):
    method get_tt_wid (line 72) | async def get_tt_wid(
  function test (line 96) | async def test():

FILE: src/encrypt/verifyFp.py
  class VerifyFp (line 14) | class VerifyFp:
    method get_verify_fp (line 33) | def get_verify_fp(timestamp: int = None):

FILE: src/encrypt/webID.py
  class WebId (line 15) | class WebId:
    method get_web_id (line 21) | async def get_web_id(
  function test (line 47) | async def test():

FILE: src/encrypt/xBogus.py
  class XBogus (line 11) | class XBogus:
    method disturb_array (line 22) | def disturb_array(a, b, e, d, c, f, t, n, o, i, r, _, x, u, s, l, v, h...
    method generate_garbled_1 (line 46) | def generate_garbled_1(a, b, e, d, c, f, t, n, o, i, r, _, x, u, s, l,...
    method generate_num (line 70) | def generate_num(text):
    method generate_garbled_2 (line 77) | def generate_garbled_2(a, b, c):
    method generate_garbled_3 (line 81) | def generate_garbled_3(a, b):
    method calculate_md5 (line 103) | def calculate_md5(self, input_string):
    method md5_to_array (line 115) | def md5_to_array(self, md5_str):
    method process_url_path (line 125) | def process_url_path(self, url_path):
    method generate_str (line 130) | def generate_str(self, num):
    method handle_ua (line 136) | def handle_ua(a, b):
    method generate_ua_array (line 156) | def generate_ua_array(self, user_agent: str, params: int) -> list:
    method generate_x_bogus (line 162) | def generate_x_bogus(
    method get_x_bogus (line 197) | def get_x_bogus(
  class XBogusTikTok (line 207) | class XBogusTikTok(XBogus):

FILE: src/encrypt/xGnarly.py
  class XGnarly (line 8) | class XGnarly:
    method __init__ (line 110) | def __init__(self):
    method _init_prng_state (line 117) | def _init_prng_state(self):
    method _u32 (line 144) | def _u32(cls, x: int) -> int:
    method _rotl (line 148) | def _rotl(cls, x: int, n: int) -> int:
    method _quarter (line 153) | def _quarter(cls, st: list[int], a: int, b: int, c: int, d: int):
    method _chacha_block (line 164) | def _chacha_block(cls, state: list[int], rounds: int) -> list[int]:
    method _bump_counter (line 184) | def _bump_counter(self):
    method rand (line 188) | def rand(self) -> float:
    method _num_to_bytes (line 201) | def _num_to_bytes(val: int) -> list[int]:
    method _be_int_from_str (line 207) | def _be_int_from_str(s: str) -> int:
    method _encrypt_chacha (line 215) | def _encrypt_chacha(self, key_words: list[int], rounds: int, data: lis...
    method _ab22 (line 261) | def _ab22(self, key12_words: list[int], rounds: int, s: str) -> str:
    method generate (line 268) | def generate(

FILE: src/extract/extractor.py
  class Extractor (line 43) | class Extractor:
    method __init__ (line 76) | def __init__(self, params: "Parameter"):
    method get_user_info (line 91) | def get_user_info(self, data: dict) -> dict:
    method get_user_info_tiktok (line 102) | def get_user_info_tiktok(self, data: dict) -> dict:
    method generate_data_object (line 114) | def generate_data_object(
    method safe_extract (line 130) | def safe_extract(
    method run (line 152) | async def run(
    method __batch (line 164) | async def __batch(
    method __condition_filter (line 205) | def __condition_filter(
    method __summary_detail (line 212) | def __summary_detail(
    method __extract_batch (line 219) | def __extract_batch(
    method __extract_batch_tiktok (line 235) | def __extract_batch_tiktok(
    method __extract_extra_info (line 251) | def __extract_extra_info(
    method __extract_extra_info_tiktok (line 262) | def __extract_extra_info_tiktok(
    method __extract_commodity_data (line 270) | def __extract_commodity_data(
    method __extract_game_data (line 277) | def __extract_game_data(
    method __extract_description (line 284) | def __extract_description(self, data: SimpleNamespace) -> str:
    method __clean_description (line 293) | def __clean_description(self, desc: str) -> str:
    method __format_date (line 296) | def __format_date(
    method __extract_detail_info (line 305) | def __extract_detail_info(
    method __extract_detail_info_tiktok (line 322) | def __extract_detail_info_tiktok(
    method __classifying_detail (line 339) | def __classifying_detail(
    method __classifying_detail_tiktok (line 354) | def __classifying_detail_tiktok(
    method __extract_additional_info (line 368) | def __extract_additional_info(
    method __generate_link (line 382) | def __generate_link(
    method __clean_share_url (line 400) | def __clean_share_url(url: str) -> str:
    method __extract_image_info (line 406) | def __extract_image_info(
    method __extract_image_info_tiktok (line 444) | def __extract_image_info_tiktok(
    method __set_blank_data (line 463) | def __set_blank_data(
    method __extract_video_info (line 476) | def __extract_video_info(
    method __classify_slides_item (line 494) | def __classify_slides_item(
    method __extract_video_download (line 504) | def __extract_video_download(
    method __extract_video_info_tiktok (line 566) | def __extract_video_info_tiktok(
    method __extract_video_download_tiktok (line 595) | def __extract_video_download_tiktok(
    method time_conversion (line 656) | def time_conversion(time_: int) -> str:
    method time_conversion_tiktok (line 661) | def time_conversion_tiktok(seconds: int) -> str:
    method __extract_text_extra (line 666) | def __extract_text_extra(
    method __extract_text_extra_tiktok (line 678) | def __extract_text_extra_tiktok(
    method __extract_cover (line 690) | def __extract_cover(
    method __extract_cover_tiktok (line 708) | def __extract_cover_tiktok(
    method __extract_music (line 722) | def __extract_music(
    method __extract_statistics (line 747) | def __extract_statistics(self, item: dict, data: SimpleNamespace) -> N...
    method __extract_statistics_tiktok (line 756) | def __extract_statistics_tiktok(
    method __extract_tags (line 769) | def __extract_tags(
    method __extract_tags_tiktok (line 779) | def __extract_tags_tiktok(
    method __extract_account_info (line 789) | def __extract_account_info(
    method __extract_account_info_tiktok (line 807) | def __extract_account_info_tiktok(
    method __extract_nickname_info (line 821) | def __extract_nickname_info(
    method preprocessing_data (line 837) | def preprocessing_data(
    method __select_item (line 933) | def __select_item(
    method __extract_pretreatment_data (line 946) | def __extract_pretreatment_data(
    method __platform_classify_detail (line 969) | def __platform_classify_detail(
    method __detail (line 992) | async def __detail(
    method __comment (line 1019) | async def __comment(
    method __extract_comments_data (line 1049) | def __extract_comments_data(
    method extract_reply_ids (line 1081) | def extract_reply_ids(cls, data: list[dict]) -> list[str]:
    method __filter_reply_ids (line 1100) | def __filter_reply_ids(container: SimpleNamespace):
    method __live (line 1104) | async def __live(
    method __extract_live_data (line 1124) | def __extract_live_data(
    method __extract_live_data_tiktok (line 1156) | def __extract_live_data_tiktok(
    method __user (line 1178) | async def __user(
    method __extract_user_data (line 1201) | def __extract_user_data(
    method __search (line 1255) | async def __search(
    method __search_general (line 1269) | async def __search_general(
    method __search_result_classify (line 1289) | def __search_result_classify(
    method __search_user (line 1311) | async def __search_user(
    method __deal_search_user_live (line 1332) | def __deal_search_user_live(
    method __search_live (line 1365) | async def __search_live(
    method __deal_search_live (line 1381) | def __deal_search_live(
    method __hot (line 1393) | async def __hot(
    method __deal_hot_data (line 1404) | def __deal_hot_data(self, container: list, data: SimpleNamespace):
    method __record_data (line 1419) | async def __record_data(self, record, data: list[dict]):
    method __extract_values (line 1425) | def __extract_values(record, data: dict) -> list:
    method __date_filter (line 1429) | def __date_filter(container: SimpleNamespace):
    method source_date_filter (line 1441) | def source_date_filter(
    method __source_date_filter (line 1461) | def __source_date_filter(
    method extract_mix_id (line 1480) | def extract_mix_id(cls, data: dict) -> str:
    method __extract_item_records (line 1484) | def __extract_item_records(self, data: list[dict]):
    method extract_mix_collect_info (line 1490) | def extract_mix_collect_info(cls, data: list[dict]) -> list[dict]:
    method extract_collects_info (line 1501) | def extract_collects_info(cls, data: list[dict]) -> list[dict]:
    method __clean_extract_data (line 1512) | def __clean_extract_data(data: list[dict], key: str) -> list[dict]:
    method __music (line 1516) | async def __music(
    method __extract_collection_music (line 1540) | def __extract_collection_music(

FILE: src/interface/account.py
  class Account (line 12) | class Account(API):
    method __init__ (line 16) | def __init__(
    method run (line 43) | async def run(
    method run_single (line 99) | async def run_single(
    method run_batch (line 125) | async def run_batch(
    method early_stop (line 154) | async def early_stop(self):
    method generate_params (line 163) | def generate_params(
    method generate_favorite_params (line 173) | def generate_favorite_params(self) -> dict:
    method generate_post_params (line 186) | def generate_post_params(self) -> dict:
    method check_type (line 200) | def check_type(self, tab: str, pages: int) -> tuple[str, bool, int]:
    method check_earliest (line 212) | def check_earliest(self, date_: str | float | int) -> date:
    method check_latest (line 215) | def check_latest(self, date_: str | float | int) -> date:
    method check_date (line 218) | def check_date(
    method check_response (line 242) | def check_response(
  function test (line 270) | async def test():

FILE: src/interface/account_tiktok.py
  class AccountTikTok (line 11) | class AccountTikTok(
    method __init__ (line 18) | def __init__(
    method run (line 48) | async def run(
    method run_batch (line 95) | async def run_batch(
    method generate_favorite_params (line 123) | def generate_favorite_params(self) -> dict:
    method generate_post_params (line 126) | def generate_post_params(self) -> dict:
  function test (line 138) | async def test():

FILE: src/interface/collection.py
  class Collection (line 11) | class Collection(API):
    method __init__ (line 12) | def __init__(
    method run (line 32) | async def run(
    method generate_params (line 64) | def generate_params(
    method generate_data (line 73) | def generate_data(
    method request_data (line 81) | async def request_data(
  function test (line 106) | async def test():

FILE: src/interface/collects.py
  class Collects (line 12) | class Collects(API):
    method __init__ (line 13) | def __init__(
    method generate_params (line 29) | def generate_params(
    method run (line 39) | async def run(
  class CollectsDetail (line 70) | class CollectsDetail(Collection, API):
    method __init__ (line 71) | def __init__(
    method generate_params (line 91) | def generate_params(
    method run (line 102) | async def run(
  class CollectsMix (line 135) | class CollectsMix(API):
    method __init__ (line 136) | def __init__(
    method generate_params (line 152) | def generate_params(
    method run (line 162) | async def run(
  class CollectsSeries (line 195) | class CollectsSeries(CollectsMix):
    method __init__ (line 196) | def __init__(
    method run (line 218) | async def run(
  class CollectsMusic (line 249) | class CollectsMusic(CollectsMix):
    method __init__ (line 250) | def __init__(
    method run (line 272) | async def run(
  function test (line 303) | async def test():

FILE: src/interface/comment.py
  class Comment (line 12) | class Comment(API):
    method __init__ (line 13) | def __init__(
    method generate_params (line 41) | def generate_params(
    method run (line 57) | async def run(
    method run_batch (line 89) | async def run_batch(
    method update_progress (line 122) | async def update_progress(
    method run_reply (line 154) | async def run_reply(
    method check_response (line 179) | def check_response(
  class Reply (line 205) | class Reply(Comment):
    method __init__ (line 206) | def __init__(
    method generate_params (line 234) | def generate_params(
    method run (line 250) | async def run(
    method run_batch (line 281) | async def run_batch(
    method check_response (line 323) | def check_response(
  function test (line 344) | async def test():

FILE: src/interface/comment_tiktok.py
  class CommentTikTok (line 12) | class CommentTikTok(Comment, APITikTok):
    method __init__ (line 13) | def __init__(
    method generate_params (line 30) | def generate_params(
  class ReplyTikTok (line 44) | class ReplyTikTok(Reply, CommentTikTok, APITikTok):
    method __init__ (line 45) | def __init__(
    method generate_params (line 72) | def generate_params(
  function test (line 85) | async def test():

FILE: src/interface/detail.py
  class Detail (line 13) | class Detail(API):
    method __init__ (line 14) | def __init__(
    method generate_params (line 26) | def generate_params(
    method run (line 35) | async def run(
    method check_response (line 65) | def check_response(
  function test (line 86) | async def test():

FILE: src/interface/detail_tiktok.py
  class DetailTikTok (line 11) | class DetailTikTok(APITikTok):
    method __init__ (line 12) | def __init__(
    method generate_params (line 24) | def generate_params(
    method run (line 31) | async def run(
    method check_response (line 61) | def check_response(
  function test (line 82) | async def test():

FILE: src/interface/hashtag.py
  class HashTag (line 13) | class HashTag(API):
    method __init__ (line 14) | def __init__(
    method run (line 24) | async def run(self, *args, **kwargs):
  function test (line 28) | async def test():

FILE: src/interface/hot.py
  class Hot (line 15) | class Hot(API):
    method __init__ (line 39) | def __init__(
    method generate_params (line 56) | def generate_params(
    method run (line 68) | async def run(
    method check_response (line 103) | def check_response(
  function test (line 125) | async def test():

FILE: src/interface/info.py
  class Info (line 12) | class Info(API):
    method __init__ (line 13) | def __init__(
    method run (line 31) | async def run(
    method run_single (line 43) | async def run_single(
    method check_response (line 55) | def check_response(
    method __generate_data (line 66) | def __generate_data(
  function test (line 77) | async def test():

FILE: src/interface/info_tiktok.py
  class InfoTikTok (line 11) | class InfoTikTok(APITikTok):
    method __init__ (line 12) | def __init__(
    method run (line 28) | async def run(
    method run_single (line 38) | async def run_single(
    method check_response (line 47) | def check_response(
    method append_response (line 58) | def append_response(
    method generate_params (line 66) | def generate_params(
  function test (line 78) | async def test():

FILE: src/interface/live.py
  class Live (line 11) | class Live(API):
    method __init__ (line 15) | def __init__(
    method run (line 30) | async def run(
    method with_web_rid (line 42) | async def with_web_rid(self) -> dict:
    method with_room_id (line 70) | async def with_room_id(self) -> dict:
  function test (line 85) | async def test():

FILE: src/interface/live_tiktok.py
  class LiveTikTok (line 12) | class LiveTikTok(APITikTok):
    method __init__ (line 15) | def __init__(
    method run (line 26) | async def run(
    method with_room_id (line 34) | async def with_room_id(self) -> dict:
    method __generate_room_id_data (line 42) | def __generate_room_id_data(
    method check_response (line 50) | def check_response(
  function test (line 62) | async def test():

FILE: src/interface/mix.py
  class Mix (line 15) | class Mix(API):
    method __init__ (line 16) | def __init__(
    method generate_params (line 43) | def generate_params(
    method run (line 54) | async def run(
    method __get_mix_id (line 88) | async def __get_mix_id(self):
  function test (line 93) | async def test():

FILE: src/interface/mix_tiktok.py
  class MixTikTok (line 11) | class MixTikTok(APITikTok):
    method __init__ (line 12) | def __init__(
    method generate_params (line 34) | def generate_params(
    method run (line 44) | async def run(
  class MixListTikTok (line 75) | class MixListTikTok(APITikTok):
    method __init__ (line 76) | def __init__(
    method generate_params (line 94) | def generate_params(
    method run (line 103) | async def run(
  function test (line 134) | async def test():

FILE: src/interface/search.py
  class Search (line 15) | class Search(API):
    method __init__ (line 134) | def __init__(
    method run (line 183) | async def run(self, single_page=False, *args, **kwargs):
    method generate_filter_selected (line 208) | def generate_filter_selected(
    method generate_search_filter_value (line 232) | def generate_search_filter_value(
    method _generate_params_general (line 250) | def _generate_params_general(
    method _generate_params_video (line 279) | def _generate_params_video(
    method _generate_params_user (line 323) | def _generate_params_user(
    method _generate_params_live (line 334) | def _generate_params_live(
    method check_response (line 357) | def check_response(
    method append_response_video (line 395) | def append_response_video(
  function test (line 403) | async def test():

FILE: src/interface/slides.py
  class Slides (line 15) | class Slides(API):
    method __init__ (line 16) | def __init__(
    method run (line 28) | async def run(self, *args, **kwargs):
  function test (line 32) | async def test():

FILE: src/interface/template.py
  class API (line 27) | class API:
    method __init__ (line 66) | def __init__(
    method set_temp_cookie (line 91) | def set_temp_cookie(self, cookie: str = ""):
    method generate_params (line 95) | def generate_params(
    method __generate_params (line 100) | def __generate_params(
    method generate_data (line 107) | def generate_data(self, *args, **kwargs) -> dict:
    method run (line 110) | async def run(
    method run_single (line 157) | async def run_single(
    method run_batch (line 184) | async def run_batch(
    method check_response (line 221) | def check_response(
    method set_referer (line 245) | def set_referer(self, url: str = None) -> None:
    method request_data (line 248) | async def request_data(
    method request_data_get (line 308) | async def request_data_get(
    method request_data_get_proxy (line 332) | async def request_data_get_proxy(
    method request_data_post (line 360) | async def request_data_post(
    method request_data_post_proxy (line 380) | async def request_data_post_proxy(
    method __return_response (line 402) | async def __return_response(self, response):
    method __record_request_messages (line 415) | def __record_request_messages(
    method deal_url_params (line 431) | def deal_url_params(
    method summary_works (line 447) | def summary_works(
    method init_progress_object (line 457) | def init_progress_object(
    method progress_object (line 466) | def progress_object(self):
    method __general_progress_object (line 470) | def __general_progress_object(self):
    method __fake_progress_object (line 487) | def __fake_progress_object(*args, **kwargs):
    method append_response (line 490) | def append_response(
  class APITikTok (line 503) | class APITikTok(API):
    method __init__ (line 541) | def __init__(
    method request_data (line 557) | async def request_data(
    method deal_url_params (line 581) | def deal_url_params(

FILE: src/interface/user.py
  class User (line 12) | class User(API):
    method __init__ (line 13) | def __init__(
    method run (line 27) | async def run(self, *args, **kwargs):
    method run_batch (line 33) | async def run_batch(
    method check_response (line 49) | def check_response(
    method generate_params (line 68) | def generate_params(
  function test (line 82) | async def test():

FILE: src/link/extractor.py
  class Extractor (line 13) | class Extractor:
    method __init__ (line 54) | def __init__(
    method run (line 65) | async def run(
    method get_html_data (line 88) | async def get_html_data(
    method detail (line 101) | def detail(
    method user (line 107) | def user(
    method mix (line 115) | def mix(
    method live (line 125) | async def live(
    method __extract_detail (line 137) | def __extract_detail(
    method extract_sec_user_id (line 150) | def extract_sec_user_id(urls: list[str]) -> list[list]:
    method extract_info (line 161) | def extract_info(pattern, urls: str, index=1) -> list[str]:
  class ExtractorTikTok (line 166) | class ExtractorTikTok(Extractor):
    method __init__ (line 183) | def __init__(self, params: "Parameter"):
    method run (line 189) | async def run(
    method detail (line 216) | async def detail(
    method user (line 222) | async def user(
    method __extract_detail (line 230) | def __extract_detail(
    method mix (line 238) | async def mix(
    method live (line 249) | async def live(

FILE: src/link/requester.py
  class Requester (line 15) | class Requester:
    method __init__ (line 18) | def __init__(
    method run (line 30) | async def run(
    method request_url (line 52) | async def request_url(
    method request_url_head (line 95) | async def request_url_head(
    method request_url_head_proxy (line 104) | def request_url_head_proxy(
    method request_url_get (line 118) | async def request_url_get(
    method request_url_get_proxy (line 129) | def request_url_get_proxy(

FILE: src/manager/cache.py
  class Cache (line 14) | class Cache:
    method __init__ (line 15) | def __init__(
    method update_cache (line 29) | async def update_cache(
    method has_cache (line 56) | async def has_cache(self, id_: str) -> dict:
    method __check_file (line 59) | def __check_file(
    method __rename_folder (line 100) | def __rename_folder(
    method __rename_works_folder (line 116) | def __rename_works_folder(
    method __scan_file (line 137) | def __scan_file(
    method __batch_rename (line 179) | def __batch_rename(
    method __rename_file (line 193) | def __rename_file(
    method __rename (line 214) | def __rename(

FILE: src/manager/database.py
  class Database (line 12) | class Database:
    method __init__ (line 15) | def __init__(
    method __connect_database (line 22) | async def __connect_database(self):
    method __create_table (line 31) | async def __create_table(self):
    method __write_default_config (line 51) | async def __write_default_config(self):
    method __write_default_option (line 57) | async def __write_default_option(self):
    method read_config_data (line 61) | async def read_config_data(self):
    method read_option_data (line 65) | async def read_option_data(self):
    method update_config_data (line 69) | async def update_config_data(
    method update_option_data (line 79) | async def update_option_data(
    method update_mapping_data (line 89) | async def update_mapping_data(self, id_: str, name: str, mark: str):
    method read_mapping_data (line 96) | async def read_mapping_data(self, id_: str):
    method has_download_data (line 102) | async def has_download_data(self, id_: str) -> bool:
    method write_download_data (line 106) | async def write_download_data(self, id_: str):
    method delete_download_data (line 112) | async def delete_download_data(self, ids: list | tuple | str):
    method __delete_download_data (line 120) | async def __delete_download_data(self, id_: str):
    method delete_all_download_data (line 123) | async def delete_all_download_data(self):
    method __aenter__ (line 127) | async def __aenter__(self):
    method close (line 132) | async def close(self):
    method __aexit__ (line 137) | async def __aexit__(self, exc_type, exc_value, traceback):
    method compatible (line 140) | def compatible(self):

FILE: src/manager/recorder.py
  class __DownloadRecorder (line 21) | class __DownloadRecorder:
    method __init__ (line 25) | def __init__(
    method __get_set (line 36) | def __get_set(self) -> set:
    method __read_file (line 39) | def __read_file(self):
    method __save_file (line 48) | def __save_file(self, file):
    method update_id (line 51) | def update_id(self, id_):
    method __extract_ids (line 55) | def __extract_ids(self, ids: str) -> list[str]:
    method delete_ids (line 63) | def delete_ids(self, ids: str) -> None:
    method backup_file (line 70) | def backup_file(self):
    method close (line 76) | def close(self):
    method __restore_data (line 83) | def __restore_data(self, ids: set) -> set:
  class DownloadRecorder (line 118) | class DownloadRecorder:
    method __init__ (line 121) | def __init__(self, database: "Database", switch: bool, console: "Color...
    method has_id (line 126) | async def has_id(self, id_: str) -> bool:
    method update_id (line 131) | async def update_id(self, id_: str):
    method delete_id (line 135) | async def delete_id(self, id_: str) -> None:
    method delete_ids (line 139) | async def delete_ids(self, ids: str) -> None:
    method __extract_ids (line 146) | def __extract_ids(self, ids: str) -> list[str]:

FILE: src/models/account.py
  class Account (line 6) | class Account(APIModel):
  class AccountTiktok (line 19) | class AccountTiktok(Account):

FILE: src/models/base.py
  class APIModel (line 4) | class APIModel(BaseModel):

FILE: src/models/comment.py
  class Comment (line 6) | class Comment(APIModel):

FILE: src/models/detail.py
  class Detail (line 4) | class Detail(APIModel):
  class DetailTikTok (line 8) | class DetailTikTok(Detail):

FILE: src/models/live.py
  class Live (line 4) | class Live(APIModel):
  class LiveTikTok (line 10) | class LiveTikTok(APIModel):

FILE: src/models/mix.py
  class Mix (line 6) | class Mix(APIModel):
  class MixTikTok (line 16) | class MixTikTok(APIModel):

FILE: src/models/reply.py
  class Reply (line 6) | class Reply(APIModel):

FILE: src/models/response.py
  class DataResponse (line 6) | class DataResponse(BaseModel):
    method time (line 13) | def time(self) -> str:
  class UrlResponse (line 18) | class UrlResponse(BaseModel):
    method time (line 25) | def time(self) -> str:

FILE: src/models/search.py
  function _ (line 11) | def _(x):
  class BaseSearch (line 15) | class BaseSearch(APIModel):
    method keyword_validator (line 32) | def keyword_validator(cls, v):
  class GeneralSearch (line 38) | class GeneralSearch(BaseSearch):
    method val_number (line 78) | def val_number(cls, value: str | int) -> int:
  class VideoSearch (line 82) | class VideoSearch(BaseSearch):
    method val_number (line 112) | def val_number(cls, value: str | int) -> int:
  class UserSearch (line 116) | class UserSearch(BaseSearch):
    method val_number (line 135) | def val_number(cls, value: str | int) -> int:
  class LiveSearch (line 139) | class LiveSearch(BaseSearch):

FILE: src/models/settings.py
  class AccountUrl (line 6) | class AccountUrl(BaseModel):
  class MixUrl (line 15) | class MixUrl(BaseModel):
  class OwnerUrl (line 21) | class OwnerUrl(BaseModel):
  class BrowserInfo (line 29) | class BrowserInfo(BaseModel):
  class TikTokBrowserInfo (line 46) | class TikTokBrowserInfo(BaseModel):
  class Settings (line 65) | class Settings(BaseModel):
    class Config (line 104) | class Config:

FILE: src/models/share.py
  class ShortUrl (line 4) | class ShortUrl(BaseModel):

FILE: src/module/cookie.py
  class Cookie (line 14) | class Cookie:
    method __init__ (line 22) | def __init__(self, settings: "Settings", console: "ColorfulConsole"):
    method run (line 30) | def run(
    method extract (line 45) | def extract(
    method __check_state (line 64) | def __check_state(self, items: dict, platform: str) -> None:
    method save_cookie (line 74) | def save_cookie(self, cookie: dict, key="cookie") -> None:
    method validate_cookie_minimal (line 80) | def validate_cookie_minimal(cls, cookie_str: str) -> bool:

FILE: src/module/ffmpeg.py
  class FFMPEG (line 10) | class FFMPEG:
    method __init__ (line 32) | def __init__(self, path: str):
    method generate_command_darwin (line 43) | def generate_command_darwin(command: list) -> None:
    method generate_command_windows (line 53) | def generate_command_windows(command: list) -> None:
    method generate_command_linux (line 67) | def generate_command_linux(cls, command: list) -> None:
    method __check_ffmpeg_path (line 82) | def __check_ffmpeg_path(self, path: Path):
    method download (line 85) | def download(self, data: list[tuple], proxy, user_agent):
    method __generate_command (line 95) | def __generate_command(
    method __check_system_ffmpeg (line 149) | def __check_system_ffmpeg(path: Path = None):

FILE: src/module/migrate_folder.py
  class MigrateFolder (line 8) | class MigrateFolder:
    method __init__ (line 9) | def __init__(
    method compatible (line 17) | def compatible(self):

FILE: src/module/register.py
  class __Register (line 29) | class __Register:
    method __init__ (line 37) | def __init__(
    method __check_progress_object (line 85) | def __check_progress_object(self):
    method generate_qr_code (line 101) | def generate_qr_code(self, url: str):
    method _open_qrcode_image (line 114) | def _open_qrcode_image(self):
    method get_qr_code (line 122) | async def get_qr_code(self):
    method __set_ms_token (line 142) | async def __set_ms_token(self):
    method check_register (line 153) | async def check_register(self, token):
    method get_cookie (line 198) | async def get_cookie(self, url, cookie):
    method request_data (line 206) | async def request_data(self, json=True, **kwargs):
    method run (line 219) | async def run(

FILE: src/module/tiktok_account_index.py
  class __TikTokAccount (line 11) | class __TikTokAccount:
    method __init__ (line 22) | def __init__(self, path: str):
    method run (line 25) | def run(self) -> list:
    method __read_html_file (line 32) | def __read_html_file(self, items) -> list:
    method __extract_id_data (line 40) | def __extract_id_data(self, html: str) -> (str, str, list[str]):
    method __extract_uid (line 47) | def __extract_uid(self, text: list):
    method __extract_nickname (line 53) | def __extract_nickname(text: list):

FILE: src/module/tiktok_unofficial.py
  class DetailTikTokUnofficial (line 21) | class DetailTikTokUnofficial:
    method __init__ (line 22) | def __init__(
    method run (line 40) | async def run(
    method request_data_get (line 49) | async def request_data_get(
    method check_response (line 65) | def check_response(
  class DetailTikTokExtractor (line 77) | class DetailTikTokExtractor:
    method __init__ (line 78) | def __init__(self, params: "Parameter"):
    method __clean_description (line 82) | def __clean_description(self, desc: str) -> str:
    method __format_date (line 85) | def __format_date(
    method run (line 94) | def run(self, data: dict) -> dict:
    method extract_detail_tiktok (line 103) | def extract_detail_tiktok(
    method extract_author_tiktok (line 121) | def extract_author_tiktok(
    method extract_music_tiktok (line 130) | def extract_music_tiktok(
    method extract_statistics_tiktok (line 140) | def extract_statistics_tiktok(
  function test (line 152) | async def test():

FILE: src/record/base.py
  class BaseLogger (line 19) | class BaseLogger:
    method __init__ (line 24) | def __init__(
    method init_check (line 41) | def init_check(
    method check_root (line 53) | def check_root(self, root: str, default: Path) -> Path:
    method check_name (line 63) | def check_name(self, name: str) -> str:
    method check_folder (line 76) | def check_folder(folder: str) -> str:
    method run (line 79) | def run(self, *args, **kwargs):
    method info (line 82) | def info(self, text: str, output=True, **kwargs):
    method warning (line 86) | def warning(self, text: str, output=True, **kwargs):
    method error (line 90) | def error(self, text: str, output=True, **kwargs):
    method debug (line 94) | def debug(self, text: str, **kwargs):
    method print (line 98) | def print(self, text: str, style=GENERAL, **kwargs) -> None:

FILE: src/record/logger.py
  class LoggerManager (line 21) | class LoggerManager(BaseLogger):
    method __init__ (line 26) | def __init__(
    method run (line 31) | def run(
    method info (line 53) | def info(self, text: str, output=True, **kwargs):
    method warning (line 58) | def warning(self, text: str, output=True, **kwargs):
    method error (line 63) | def error(self, text: str, output=True, **kwargs):
    method debug (line 68) | def debug(self, text: str, **kwargs):
    method compatible (line 73) | def compatible(

FILE: src/storage/csv.py
  class CSVLogger (line 15) | class CSVLogger(BaseTextLogger):
    method __init__ (line 21) | def __init__(
    method __aenter__ (line 41) | async def __aenter__(self):
    method __aexit__ (line 47) | async def __aexit__(self, exc_type, exc_val, exc_tb):
    method title (line 50) | async def title(self):
    method _save (line 55) | async def _save(self, data, *args, **kwargs):

FILE: src/storage/manager.py
  class RecordManager (line 17) | class RecordManager:
    method run (line 618) | def run(
    method compatible (line 643) | def compatible(

FILE: src/storage/mysql.py
  class MySQLLogger (line 6) | class MySQLLogger(BaseSQLLogger):

FILE: src/storage/sql.py
  class BaseSQLLogger (line 9) | class BaseSQLLogger(BaseTextLogger):

FILE: src/storage/sqlite.py
  class SQLLogger (line 17) | class SQLLogger(BaseSQLLogger):
    method __init__ (line 20) | def __init__(
    method __aenter__ (line 42) | async def __aenter__(self):
    method __aexit__ (line 49) | async def __aexit__(self, exc_type, exc_val, exc_tb):
    method create (line 52) | async def create(self):
    method _save (line 59) | async def _save(self, data, *args, **kwargs):
    method update_sheet (line 66) | async def update_sheet(self):
    method __check_sheet_exists (line 98) | async def __check_sheet_exists(self, sheet: str) -> bool:
    method __clean_sheet_name (line 103) | def __clean_sheet_name(self, name: tuple) -> tuple:
    method __clean_characters (line 106) | def __clean_characters(self, text: str | None) -> str | None:

FILE: src/storage/text.py
  function convert_to_string (line 11) | def convert_to_string(function):
  class BaseTextLogger (line 23) | class BaseTextLogger:
    method __init__ (line 24) | def __init__(self, *args, **kwargs):
    method __aenter__ (line 27) | async def __aenter__(self):
    method __aexit__ (line 30) | async def __aexit__(self, exc_type, exc_val, exc_tb):
    method save (line 34) | async def save(self, data: "Iterable", *args, **kwargs):
    method _save (line 38) | async def _save(self, data: "Iterable", *args, **kwargs):
    method _rename (line 43) | def _rename(cls, root: Path, type_: str, old: str, new_: str) -> str:
    method __rename_file (line 54) | def __rename_file(old_file: Path, new_file: Path) -> bool:

FILE: src/storage/xlsx.py
  class XLSXLogger (line 16) | class XLSXLogger(BaseTextLogger):
    method __init__ (line 21) | def __init__(
    method __aenter__ (line 41) | async def __aenter__(self):
    method __aexit__ (line 47) | async def __aexit__(self, exc_type, exc_val, exc_tb):
    method title (line 51) | def title(self):
    method _save (line 57) | async def _save(self, data, *args, **kwargs):

FILE: src/testers/logger.py
  class Logger (line 1) | class Logger:
    method info (line 3) | def info(
    method warning (line 11) | def warning(
    method error (line 19) | def error(
    method debug (line 27) | def debug(

FILE: src/testers/params.py
  class Params (line 16) | class Params:
    method __init__ (line 20) | def __init__(self):
    method create_ini (line 55) | def create_ini(self):
    method read_ini (line 68) | def read_ini(self):
    method __aenter__ (line 96) | async def __aenter__(self):
    method __aexit__ (line 99) | async def __aexit__(self, exc_type, exc_val, exc_tb):
  function test (line 104) | async def test():

FILE: src/testers/test_format.py
  function test_cookie_str_to_dict (line 23) | def test_cookie_str_to_dict(x, y):
  function test_cookie_str_to_str (line 36) | def test_cookie_str_to_str(x, y):
  function test_cookie_dict_to_str (line 50) | def test_cookie_dict_to_str(x, y):
  function create_test_cookie_jar (line 54) | def create_test_cookie_jar():
  function test_cookie_jar_to_dict (line 88) | def test_cookie_jar_to_dict(x, y):
  function test_format_size (line 100) | def test_format_size(x, y):

FILE: src/tools/browser.py
  class Browser (line 28) | class Browser:
    method __init__ (line 58) | def __init__(self, parameters: "Parameter", cookie_object: "Cookie"):
    method run (line 71) | def run(
    method __save_cookie (line 107) | def __save_cookie(self, cookie: dict, tiktok: bool):
    method get (line 110) | def get(
    method __browser_object (line 130) | def __browser_object(cls, browser: str | int):
    method __match_browser (line 146) | def __match_browser(cls, browser: str):

FILE: src/tools/capture.py
  function capture_error_params (line 18) | def capture_error_params(function):
  function capture_error_request (line 43) | def capture_error_request(function):

FILE: src/tools/choose.py
  function choose (line 11) | def choose(

FILE: src/tools/cleaner.py
  class Cleaner (line 15) | class Cleaner:
    method __init__ (line 18) | def __init__(self):
    method default_rule (line 25) | def default_rule():
    method set_rule (line 51) | def set_rule(self, rule: dict[str, str], update=False):
    method filter (line 60) | def filter(self, text: str) -> str:
    method filter_name (line 71) | def filter_name(
    method clear_spaces (line 92) | def clear_spaces(string: str):
    method remove_control_characters (line 97) | def remove_control_characters(

FILE: src/tools/console.py
  class ColorfulConsole (line 16) | class ColorfulConsole(Console):
    method __init__ (line 17) | def __init__(self, *args, debug: bool = False, **kwargs):
    method print (line 21) | def print(self, *args, style=GENERAL, highlight=False, **kwargs):
    method info (line 24) | def info(self, *args, highlight=False, **kwargs):
    method warning (line 27) | def warning(self, *args, highlight=False, **kwargs):
    method error (line 30) | def error(self, *args, highlight=False, **kwargs):
    method debug (line 33) | def debug(self, *args, highlight=False, **kwargs):
    method input (line 37) | def input(self, prompt="", style=PROMPT, *args, **kwargs):

FILE: src/tools/error.py
  class DownloaderError (line 4) | class DownloaderError(Exception):
    method __init__ (line 5) | def __init__(
    method __str__ (line 12) | def __str__(self):
  class CacheError (line 16) | class CacheError(Exception):
    method __init__ (line 17) | def __init__(self, message: str):
    method __str__ (line 21) | def __str__(self):

FILE: src/tools/file_folder.py
  function file_switch (line 5) | def file_switch(path: Path) -> None:
  function remove_empty_directories (line 12) | def remove_empty_directories(path: Path) -> None:

FILE: src/tools/format.py
  function cookie_str_to_dict (line 5) | def cookie_str_to_dict(cookie_str: str) -> dict:
  function cookie_str_to_str (line 18) | def cookie_str_to_str(cookie_str: str) -> str:
  function cookie_dict_to_str (line 25) | def cookie_dict_to_str(cookie_dict: dict | CookieJar) -> str:
  function cookie_jar_to_dict (line 32) | def cookie_jar_to_dict(cookie_jar: CookieJar) -> dict:
  function format_size (line 36) | def format_size(size_in_bytes: int) -> str:

FILE: src/tools/list_pop.py
  function safe_pop (line 4) | def safe_pop(data: list):

FILE: src/tools/progress.py
  class FakeProgress (line 1) | class FakeProgress:
    method __init__ (line 2) | def __init__(
    method __aenter__ (line 9) | async def __aenter__(self):
    method __enter__ (line 12) | def __enter__(self):
    method __aexit__ (line 15) | async def __aexit__(self, exc_type, exc_val, exc_tb):
    method __exit__ (line 18) | def __exit__(self, exc_type, exc_val, exc_tb):
    method add_task (line 21) | def add_task(
    method update (line 28) | def update(
    method remove_task (line 35) | def remove_task(

FILE: src/tools/rename_compatible.py
  class RenameCompatible (line 5) | class RenameCompatible:
    method migration_file (line 10) | def migration_file(

FILE: src/tools/retry.py
  class Retry (line 7) | class Retry:
    method retry (line 11) | def retry(function):
    method retry_lite (line 28) | def retry_lite(function):
    method retry_limited (line 41) | def retry_limited(function):
    method retry_infinite (line 57) | def retry_infinite(function):

FILE: src/tools/session.py
  function create_client (line 17) | def create_client(
  function request_params (line 42) | async def request_params(
  function request (line 88) | async def request(

FILE: src/tools/temporary.py
  function timestamp (line 12) | def timestamp() -> str:
  function random_string (line 16) | def random_string(length: int = 10) -> str:

FILE: src/tools/timer.py
  function run_time (line 6) | def run_time(function):

FILE: src/tools/truncate.py
  function is_chinese_char (line 4) | def is_chinese_char(char: str) -> bool:
  function truncate_string (line 8) | def truncate_string(s: str, length: int = 64) -> str:
  function trim_string (line 19) | def trim_string(s: str, length: int = 64) -> str:
  function beautify_string (line 24) | def beautify_string(s: str, length: int = 64) -> str:

FILE: src/translation/translate.py
  class TranslationManager (line 8) | class TranslationManager:
    method __new__ (line 13) | def __new__(cls, *args, **kwargs):
    method __init__ (line 18) | def __init__(self, domain="tk", localedir=None):
    method get_language_code (line 28) | def get_language_code() -> str:
    method setup_translation (line 46) | def setup_translation(self, language: str = "zh_CN"):
    method switch_language (line 61) | def switch_language(self, language: str = "en_US"):
    method gettext (line 65) | def gettext(self, message):
  function _translate (line 74) | def _translate(message):
  function switch_language (line 79) | def switch_language(language: str = "en_US"):

FILE: src/tui_edition/app.py
  class App (line 4) | class App:

FILE: src/tui_edition/setting.py
  class Setting (line 4) | class Setting:

FILE: static/js/X-Bogus.js
  function _0x5cd844 (line 3) | function _0x5cd844(e) {
  function f (line 11) | function f(e, b, a) {
  function c (line 33) | function c(e) {
  function M (line 135) | function M(e, b, a) {
  function P (line 144) | function P(e, b, a, n, h, p, y, v) {
  function F (line 324) | function F(e, b, a, f, c, r, t, d) {
  function _0x5887c8 (line 389) | function _0x5887c8(e) {
  function _0x178cef (line 461) | function _0x178cef(e) {
  function sign (line 507) | function sign(e, b) {

FILE: static/js/a_bogus.js
  function rc4_encrypt (line 2) | function rc4_encrypt(plaintext, key) {
  function le (line 30) | function le(e, r) {
  function de (line 34) | function de(e) {
  function pe (line 38) | function pe(e, r, t, n) {
  function he (line 43) | function he(e, r, t, n) {
  function reset (line 48) | function reset() {
  function write (line 61) | function write(e) {
  function sum (line 85) | function sum(e, t) {
  function _compress (line 112) | function _compress(t) {
  function _fill (line 152) | function _fill() {
  function SM3 (line 166) | function SM3() {
  function result_encrypt (line 178) | function result_encrypt(long_str, num = null) {
  function get_long_int (line 226) | function get_long_int(round, long_str) {
  function gener_random (line 231) | function gener_random(random, option) {
  function generate_rc4_bb_str (line 241) | function generate_rc4_bb_str(url_search_params, user_agent, window_env_s...
  function generate_random_str (line 405) | function generate_random_str() {
  function generate_a_bogus (line 413) | function generate_a_bogus(url_search_params, user_agent) {
Condensed preview — 147 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,002K chars).
[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 709,
    "preview": "---\nname: Bug report\nabout: 报告项目问题\ntitle: '[功能异常] '\nlabels: ''\nassignees: JoeanAmier\n\n---\n\n**问题描述**\n\n清晰简洁地描述该错误是什么。\n\nA c"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 570,
    "preview": "---\nname: Feature request\nabout: 功能优化建议\ntitle: '[优化建议] '\nlabels: ''\nassignees: JoeanAmier\n\n---\n\n**功能请求**\n\n清晰简洁地描述问题是什么。例"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 325,
    "preview": "version: 2\nregistries:\n  pip_mirror:\n    type: python-index\n    url: https://mirrors.ustc.edu.cn/pypi/simple\nupdates:\n  "
  },
  {
    "path": ".github/workflows/Close_Stale_Issues_and_PRs.yaml",
    "chars": 1569,
    "preview": "name: \"自动管理过时的问题和PR\"\non:\n  schedule:\n    - cron: \"0 0 * * 6\"\n  workflow_dispatch:\n\npermissions:\n  issues: write\n  pull-r"
  },
  {
    "path": ".github/workflows/Delete_untagged_images.yml",
    "chars": 569,
    "preview": "name: 删除 GHCR Untagged 镜像\non:\n  schedule:\n    - cron: \"0 0 15 * *\"\n  release:\n    types: [ published ]\n  workflow_dispat"
  },
  {
    "path": ".github/workflows/Manually_build_executable_programs.yml",
    "chars": 1336,
    "preview": "name: 构建可执行文件\n\non:\n  workflow_dispatch:\n\njobs:\n  build:\n    name: 构建于 ${{ matrix.os }}\n    runs-on: ${{ matrix.os }}\n   "
  },
  {
    "path": ".github/workflows/Manually_docker_image.yml",
    "chars": 2686,
    "preview": "name: 构建并发布 Docker 镜像\n\non:\n  workflow_dispatch:\n    inputs:\n      is_beta:\n        type: boolean\n        required: true\n"
  },
  {
    "path": ".github/workflows/Release_build_executable_program.yml",
    "chars": 1648,
    "preview": "name: 自动构建并发布可执行文件\n\non:\n  release:\n    types: [ published ]\n\npermissions:\n  contents: write\n  discussions: write\n\njobs:\n"
  },
  {
    "path": ".github/workflows/Release_docker_image.yml",
    "chars": 1525,
    "preview": "name: 自动构建并发布 Docker 镜像\n\non:\n  release:\n    types: [ published ]\n\npermissions:\n  contents: read\n  packages: write\n  atte"
  },
  {
    "path": ".gitignore",
    "chars": 76,
    "preview": "__pycache__/\n*.pyc\n/.venv/\n/.ruff_cache/\n/.idea/\n/.run/\n/Volume/\n!/.github/\n"
  },
  {
    "path": ".python-version",
    "chars": 5,
    "preview": "3.12\n"
  },
  {
    "path": "Dockerfile",
    "chars": 982,
    "preview": "# ---- 阶段 1: 构建器 (Builder) ----\n# 使用一个功能完整的镜像,它包含编译工具或可以轻松安装它们\nFROM python:3.12-bullseye as builder\n\n# 安装编译 uvloop 和 htt"
  },
  {
    "path": "README.md",
    "chars": 15764,
    "preview": "<div align=\"center\">\n<img src=\"./static/images/DouK-Downloader.png\" alt=\"DouK-Downloader\" height=\"256\" width=\"256\"><br>\n"
  },
  {
    "path": "README_EN.md",
    "chars": 29126,
    "preview": "<div align=\"center\">\n<img src=\"./static/images/DouK-Downloader.png\" alt=\"DouK-Downloader\" height=\"256\" width=\"256\"><br>\n"
  },
  {
    "path": "docs/Cookie获取教程.md",
    "chars": 936,
    "preview": "# Cookie 获取教程\n\n本教程仅演示部分能够获取所需 `Cookie` 的方法,仍有其他方法能够获取所需 `Cookie`;本教程使用的浏览器为 `Microsoft Edge`\n,部分浏览器的开发人员工具可能不支持中文语言。\n\n**"
  },
  {
    "path": "docs/DouK-Downloader文档.md",
    "chars": 46776,
    "preview": "<div align=\"center\">\n<img src=\"https://github.com/JoeanAmier/TikTokDownloader/blob/master/static/images/DouK-Downloader."
  },
  {
    "path": "docs/Release_Notes.md",
    "chars": 352,
    "preview": "**更新内容:**\n\n1. API 模式搜索接口增加 `offset` 和 `count` 参数\n2. 修复部分 TikTok 账号提取 sec_user_id 失败的问题\n3. 修复 API 模式搜索接口多页数据报错的问题\n4. 修复 A"
  },
  {
    "path": "license",
    "chars": 35149,
    "preview": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free "
  },
  {
    "path": "locale/README.md",
    "chars": 1197,
    "preview": "# 命令参考\n\n**运行命令前,确保已经安装了 `gettext` 软件包,并配置好环境变量。**\n\n**Before running the command, ensure that the `gettext` package is in"
  },
  {
    "path": "locale/en_US/LC_MESSAGES/tk.po",
    "chars": 92724,
    "preview": "# English translations for DouK-Downloader package.\n# Copyright (C) 2024 THE DouK-Downloader'S COPYRIGHT HOLDER\n# This f"
  },
  {
    "path": "locale/generate_path.py",
    "chars": 459,
    "preview": "from pathlib import Path\n\nROOT = Path(__file__).resolve().parent.parent\n\n\ndef find_python_files(dir_, file):\n    with op"
  },
  {
    "path": "locale/po_to_mo.py",
    "chars": 566,
    "preview": "from pathlib import Path\nfrom subprocess import run\n\nROOT = Path(__file__).resolve().parent\n\n\ndef scan_directory():\n    "
  },
  {
    "path": "locale/tk.pot",
    "chars": 62801,
    "preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
  },
  {
    "path": "locale/zh_CN/LC_MESSAGES/tk.po",
    "chars": 62903,
    "preview": "# Chinese translations for DouK-Downloader package\n# Copyright (C) 2024 THE DouK-Downloader'S COPYRIGHT HOLDER\n# This fi"
  },
  {
    "path": "main.py",
    "chars": 381,
    "preview": "from asyncio import CancelledError\nfrom asyncio import run\n\nfrom src.application import TikTokDownloader\n\n\nasync def mai"
  },
  {
    "path": "pyproject.toml",
    "chars": 2648,
    "preview": "[project]\nname = \"DouK-Downloader\"\nversion = \"5.8\"\ndescription = \"TikTok 发布/喜欢/合辑/直播/视频/图集/音乐;抖音发布/喜欢/收藏/收藏夹/视频/图集/实况/直播"
  },
  {
    "path": "requirements.txt",
    "chars": 968,
    "preview": "# This file was autogenerated by uv via the following command:\n#    uv pip compile pyproject.toml --no-deps --no-strip-e"
  },
  {
    "path": "src/application/TikTokDownloader.py",
    "chars": 13868,
    "preview": "from asyncio import CancelledError, run\nfrom threading import Event, Thread\nfrom time import sleep\n\nfrom httpx import Re"
  },
  {
    "path": "src/application/__init__.py",
    "chars": 79,
    "preview": "from .TikTokDownloader import TikTokDownloader\n\n__all__ = [\"TikTokDownloader\"]\n"
  },
  {
    "path": "src/application/main_monitor.py",
    "chars": 4077,
    "preview": "from contextlib import suppress\nfrom typing import TYPE_CHECKING\nfrom asyncio import Event, create_task, gather, sleep, "
  },
  {
    "path": "src/application/main_server.py",
    "chars": 23952,
    "preview": "from textwrap import dedent\nfrom typing import TYPE_CHECKING\n\nfrom fastapi import Depends, FastAPI, Header, HTTPExceptio"
  },
  {
    "path": "src/application/main_terminal.py",
    "chars": 66461,
    "preview": "from datetime import date, datetime\nfrom pathlib import Path\nfrom platform import system\nfrom time import time\nfrom type"
  },
  {
    "path": "src/cli_edition/__init__.py",
    "chars": 45,
    "preview": "from .main_cli import cli\n\n__all__ = [\"cli\"]\n"
  },
  {
    "path": "src/cli_edition/main_cli.py",
    "chars": 62,
    "preview": "__all__ = [\"cli\"]\n\n\nclass Cli:\n    pass\n\n\ndef cli():\n    pass\n"
  },
  {
    "path": "src/cli_edition/write.py",
    "chars": 1646,
    "preview": "from pathlib import Path\n\nfrom src.config import Settings\nfrom src.custom import PROJECT_ROOT\nfrom src.tools import Colo"
  },
  {
    "path": "src/config/__init__.py",
    "chars": 101,
    "preview": "from .parameter import Parameter\nfrom .settings import Settings\n\n__all__ = [\"Parameter\", \"Settings\"]\n"
  },
  {
    "path": "src/config/parameter.py",
    "chars": 38741,
    "preview": "from pathlib import Path\nfrom shutil import move\nfrom time import localtime, strftime\nfrom types import SimpleNamespace\n"
  },
  {
    "path": "src/config/settings.py",
    "chars": 6650,
    "preview": "from json import dump, load\nfrom json.decoder import JSONDecodeError\nfrom platform import system\nfrom shutil import move"
  },
  {
    "path": "src/custom/__init__.py",
    "chars": 1406,
    "preview": "from .function import (\n    wait,\n    failure_handling,\n    condition_filter,\n    suspend,\n    is_valid_token,\n)\nfrom .i"
  },
  {
    "path": "src/custom/function.py",
    "chars": 1588,
    "preview": "from asyncio import sleep\nfrom random import randint\nfrom typing import TYPE_CHECKING\nfrom src.translation import _\n\nif "
  },
  {
    "path": "src/custom/internal.py",
    "chars": 3985,
    "preview": "from pathlib import Path\n\nPROJECT_ROOT = Path(__file__).resolve().parent.parent.parent.joinpath(\"Volume\")\nPROJECT_ROOT.m"
  },
  {
    "path": "src/custom/static.py",
    "chars": 1544,
    "preview": "# 同时下载作品文件的最大任务数,对直播无效\nMAX_WORKERS = 4\n\n# 非法字符替换规则,key 为替换前的文本,value 为替换后的文本\nTEXT_REPLACEMENT = {\n    \" \": \" \",\n}\n\n# 服务器"
  },
  {
    "path": "src/downloader/__init__.py",
    "chars": 59,
    "preview": "from .download import Downloader\n\n__all__ = [\"Downloader\"]\n"
  },
  {
    "path": "src/downloader/download.py",
    "chars": 28800,
    "preview": "from asyncio import Semaphore, gather\nfrom datetime import datetime\nfrom pathlib import Path\nfrom shutil import move\nfro"
  },
  {
    "path": "src/encrypt/__init__.py",
    "chars": 267,
    "preview": "from .aBogus import ABogus\nfrom .device_id import DeviceId\nfrom .msToken import MsToken, MsTokenTikTok\nfrom .ttWid impor"
  },
  {
    "path": "src/encrypt/aBogus.py",
    "chars": 15557,
    "preview": "from random import choice, randint, random\nfrom re import compile\nfrom time import time\nfrom urllib.parse import quote, "
  },
  {
    "path": "src/encrypt/device_id.py",
    "chars": 1874,
    "preview": "from asyncio import run\nfrom re import compile\nfrom typing import TYPE_CHECKING, Union\n\nfrom src.custom import PARAMS_HE"
  },
  {
    "path": "src/encrypt/msToken.py",
    "chars": 14380,
    "preview": "from asyncio import run\nfrom json import dumps\nfrom random import randint\nfrom string import ascii_lowercase, ascii_uppe"
  },
  {
    "path": "src/encrypt/ttWid.py",
    "chars": 3038,
    "preview": "from asyncio import run\nfrom http import cookies\nfrom json import dumps\nfrom typing import TYPE_CHECKING, Union\n\nfrom sr"
  },
  {
    "path": "src/encrypt/verifyFp.py",
    "chars": 1888,
    "preview": "from random import random\nfrom string import ascii_lowercase\nfrom string import ascii_uppercase\nfrom string import digit"
  },
  {
    "path": "src/encrypt/webID.py",
    "chars": 1440,
    "preview": "from asyncio import run\nfrom typing import TYPE_CHECKING, Union\n\nfrom src.custom import PARAMS_HEADERS\nfrom src.tools im"
  },
  {
    "path": "src/encrypt/xBogus.py",
    "chars": 5870,
    "preview": "from base64 import b64encode\nfrom hashlib import md5\nfrom time import time\nfrom urllib.parse import quote, urlencode\n\nfr"
  },
  {
    "path": "src/encrypt/xGnarly.py",
    "chars": 9822,
    "preview": "from hashlib import md5\nfrom random import randint\nfrom time import time\n\nfrom src.custom import USERAGENT\n\n\nclass XGnar"
  },
  {
    "path": "src/extract/__init__.py",
    "chars": 58,
    "preview": "from .extractor import Extractor\n\n__all__ = [\"Extractor\"]\n"
  },
  {
    "path": "src/extract/extractor.py",
    "chars": 50145,
    "preview": "from datetime import datetime\nfrom json import dumps\nfrom time import localtime, strftime\nfrom types import SimpleNamesp"
  },
  {
    "path": "src/gui_edition/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/interface/__init__.py",
    "chars": 980,
    "preview": "from ..interface.account import Account\nfrom ..interface.account_tiktok import AccountTikTok\nfrom ..interface.collection"
  },
  {
    "path": "src/interface/account.py",
    "chars": 8300,
    "preview": "from datetime import date, datetime, timedelta\nfrom typing import TYPE_CHECKING, Callable, Coroutine, Type, Union\n\nfrom "
  },
  {
    "path": "src/interface/account_tiktok.py",
    "chars": 3988,
    "preview": "from typing import TYPE_CHECKING, Callable, Coroutine, Type, Union\n\nfrom src.interface.account import Account\nfrom src.i"
  },
  {
    "path": "src/interface/collection.py",
    "chars": 2729,
    "preview": "from typing import TYPE_CHECKING, Callable, Union\n\nfrom src.interface.template import API\nfrom src.translation import _\n"
  },
  {
    "path": "src/interface/collects.py",
    "chars": 8070,
    "preview": "from typing import TYPE_CHECKING, Callable, Union\n\nfrom src.interface.collection import Collection\nfrom src.interface.te"
  },
  {
    "path": "src/interface/comment.py",
    "chars": 9438,
    "preview": "from typing import TYPE_CHECKING, Callable, Coroutine, Type, Union\n\nfrom src.extract import Extractor\nfrom src.interface"
  },
  {
    "path": "src/interface/comment_tiktok.py",
    "chars": 2606,
    "preview": "from typing import TYPE_CHECKING, Union\n\nfrom src.interface.comment import Comment, Reply\nfrom src.interface.template im"
  },
  {
    "path": "src/interface/detail.py",
    "chars": 2258,
    "preview": "from typing import Callable\nfrom typing import TYPE_CHECKING\nfrom typing import Union\n\nfrom src.interface.template impor"
  },
  {
    "path": "src/interface/detail_tiktok.py",
    "chars": 2212,
    "preview": "from typing import TYPE_CHECKING, Callable, Union\n\nfrom src.interface.template import APITikTok\nfrom src.translation imp"
  },
  {
    "path": "src/interface/hashtag.py",
    "chars": 781,
    "preview": "from typing import TYPE_CHECKING\nfrom typing import Union\n\nfrom src.interface.template import API\n\n# from src.translatio"
  },
  {
    "path": "src/interface/hot.py",
    "chars": 3448,
    "preview": "from datetime import datetime\nfrom types import SimpleNamespace\nfrom typing import Callable\nfrom typing import TYPE_CHEC"
  },
  {
    "path": "src/interface/info.py",
    "chars": 2147,
    "preview": "from typing import TYPE_CHECKING\nfrom typing import Union\n\nfrom src.interface.template import API\nfrom src.translation i"
  },
  {
    "path": "src/interface/info_tiktok.py",
    "chars": 2141,
    "preview": "from typing import TYPE_CHECKING, Union\n\nfrom src.interface.template import APITikTok\nfrom src.translation import _\n\nif "
  },
  {
    "path": "src/interface/live.py",
    "chars": 2663,
    "preview": "from typing import TYPE_CHECKING, Union\n\nfrom src.interface.template import API\nfrom src.tools import DownloaderError\n\ni"
  },
  {
    "path": "src/interface/live_tiktok.py",
    "chars": 1727,
    "preview": "from typing import TYPE_CHECKING\nfrom typing import Union\n\nfrom src.interface.template import APITikTok\nfrom src.transla"
  },
  {
    "path": "src/interface/mix.py",
    "chars": 2552,
    "preview": "from typing import Callable\nfrom typing import TYPE_CHECKING\nfrom typing import Union\n\nfrom src.extract import Extractor"
  },
  {
    "path": "src/interface/mix_tiktok.py",
    "chars": 3678,
    "preview": "from typing import TYPE_CHECKING, Callable, Union\n\nfrom src.interface.template import APITikTok\nfrom src.translation imp"
  },
  {
    "path": "src/interface/search.py",
    "chars": 12325,
    "preview": "from json import dumps\nfrom types import SimpleNamespace\nfrom typing import TYPE_CHECKING, Union\nfrom urllib.parse impor"
  },
  {
    "path": "src/interface/slides.py",
    "chars": 982,
    "preview": "# from typing import Callable\nfrom typing import TYPE_CHECKING\nfrom typing import Union\n\nfrom src.interface.template imp"
  },
  {
    "path": "src/interface/template.py",
    "chars": 16600,
    "preview": "from time import time\nfrom typing import TYPE_CHECKING, Callable, Coroutine, Type, Union\nfrom urllib.parse import quote,"
  },
  {
    "path": "src/interface/user.py",
    "chars": 2307,
    "preview": "from typing import TYPE_CHECKING, Callable, Type, Coroutine\nfrom typing import Union\n\nfrom src.interface.template import"
  },
  {
    "path": "src/link/__init__.py",
    "chars": 105,
    "preview": "from .extractor import Extractor, ExtractorTikTok\n\n__all__ = [\n    \"Extractor\",\n    \"ExtractorTikTok\",\n]\n"
  },
  {
    "path": "src/link/extractor.py",
    "chars": 7832,
    "preview": "from re import compile\nfrom typing import TYPE_CHECKING, Union\nfrom urllib.parse import parse_qs, unquote, urlparse\n\nfro"
  },
  {
    "path": "src/link/requester.py",
    "chars": 3703,
    "preview": "from re import compile\nfrom typing import TYPE_CHECKING\n\nfrom ..custom import wait\nfrom ..tools import DownloaderError, "
  },
  {
    "path": "src/manager/__init__.py",
    "chars": 163,
    "preview": "from .cache import Cache\nfrom .database import Database\nfrom .recorder import DownloadRecorder\n\n__all__ = [\n    \"Cache\","
  },
  {
    "path": "src/manager/cache.py",
    "chars": 6169,
    "preview": "from pathlib import Path\nfrom typing import TYPE_CHECKING\n\nfrom ..tools import Retry\nfrom ..translation import _\n\nif TYP"
  },
  {
    "path": "src/manager/database.py",
    "chars": 4678,
    "preview": "from asyncio import CancelledError\nfrom contextlib import suppress\nfrom shutil import move\n\nfrom aiosqlite import Row, c"
  },
  {
    "path": "src/manager/recorder.py",
    "chars": 4596,
    "preview": "from pathlib import Path\nfrom platform import system\nfrom re import compile\nfrom typing import TYPE_CHECKING\n\nfrom ..cus"
  },
  {
    "path": "src/models/__init__.py",
    "chars": 723,
    "preview": "from .response import DataResponse, UrlResponse\nfrom .search import (\n    GeneralSearch,\n    VideoSearch,\n    UserSearch"
  },
  {
    "path": "src/models/account.py",
    "chars": 361,
    "preview": "from pydantic import Field\n\nfrom .base import APIModel\n\n\nclass Account(APIModel):\n    sec_user_id: str\n    tab: str = \"p"
  },
  {
    "path": "src/models/base.py",
    "chars": 126,
    "preview": "from pydantic import BaseModel\n\n\nclass APIModel(BaseModel):\n    cookie: str = \"\"\n    proxy: str = \"\"\n    source: bool = "
  },
  {
    "path": "src/models/comment.py",
    "chars": 317,
    "preview": "from pydantic import Field\n\nfrom .base import APIModel\n\n\nclass Comment(APIModel):\n    detail_id: str\n    pages: int = Fi"
  },
  {
    "path": "src/models/detail.py",
    "chars": 111,
    "preview": "from .base import APIModel\n\n\nclass Detail(APIModel):\n    detail_id: str\n\n\nclass DetailTikTok(Detail):\n    pass\n"
  },
  {
    "path": "src/models/live.py",
    "chars": 213,
    "preview": "from .base import APIModel\n\n\nclass Live(APIModel):\n    web_rid: str | None = None\n    # room_id: str | None = None\n    #"
  },
  {
    "path": "src/models/mix.py",
    "chars": 352,
    "preview": "from pydantic import Field\n\nfrom .base import APIModel\n\n\nclass Mix(APIModel):\n    mix_id: str | None = None\n    detail_i"
  },
  {
    "path": "src/models/reply.py",
    "chars": 249,
    "preview": "from pydantic import Field\n\nfrom .base import APIModel\n\n\nclass Reply(APIModel):\n    detail_id: str\n    comment_id: str\n "
  },
  {
    "path": "src/models/response.py",
    "chars": 588,
    "preview": "from datetime import datetime\n\nfrom pydantic import BaseModel, computed_field\n\n\nclass DataResponse(BaseModel):\n    messa"
  },
  {
    "path": "src/models/search.py",
    "chars": 2465,
    "preview": "from typing import Literal\n\nfrom pydantic import Field, field_validator\n\nfrom src.models.base import APIModel\n\ntry:\n    "
  },
  {
    "path": "src/models/settings.py",
    "chars": 2961,
    "preview": "from typing import List\n\nfrom pydantic import BaseModel, Field\n\n\nclass AccountUrl(BaseModel):\n    mark: str = \"\"\n    url"
  },
  {
    "path": "src/models/share.py",
    "chars": 94,
    "preview": "from pydantic import BaseModel\n\n\nclass ShortUrl(BaseModel):\n    text: str\n    proxy: str = \"\"\n"
  },
  {
    "path": "src/module/__init__.py",
    "chars": 352,
    "preview": "from .cookie import Cookie\nfrom .ffmpeg import FFMPEG\nfrom .migrate_folder import MigrateFolder\n\n# from .register import"
  },
  {
    "path": "src/module/cookie.py",
    "chars": 2443,
    "preview": "from typing import TYPE_CHECKING\nfrom ..tools import cookie_str_to_dict\nfrom ..translation import _\nfrom re import compi"
  },
  {
    "path": "src/module/ffmpeg.py",
    "chars": 4357,
    "preview": "from pathlib import Path\nfrom shutil import which\nfrom platform import system\nfrom subprocess import Popen, run\nfrom tex"
  },
  {
    "path": "src/module/migrate_folder.py",
    "chars": 1079,
    "preview": "from shutil import move\nfrom typing import TYPE_CHECKING\n\nif TYPE_CHECKING:\n    from ..config import Parameter\n\n\nclass M"
  },
  {
    "path": "src/module/register.py",
    "chars": 8341,
    "preview": "from platform import system\nfrom subprocess import run\nfrom time import sleep\nfrom typing import TYPE_CHECKING\nfrom urll"
  },
  {
    "path": "src/module/tiktok_account_index.py",
    "chars": 1893,
    "preview": "from pathlib import Path\nfrom re import compile\n\nfrom lxml.etree import HTML\n\nfrom src.tools import timestamp\n\n__all__ ="
  },
  {
    "path": "src/module/tiktok_unofficial.py",
    "chars": 4658,
    "preview": "from time import strftime, localtime\nfrom types import SimpleNamespace\nfrom typing import TYPE_CHECKING\nfrom typing impo"
  },
  {
    "path": "src/record/__init__.py",
    "chars": 106,
    "preview": "from .base import BaseLogger\nfrom .logger import LoggerManager\n\n__all__ = [\"LoggerManager\", \"BaseLogger\"]\n"
  },
  {
    "path": "src/record/base.py",
    "chars": 2504,
    "preview": "from pathlib import Path\nfrom time import localtime, strftime\nfrom typing import TYPE_CHECKING\n\nfrom ..custom import (\n "
  },
  {
    "path": "src/record/logger.py",
    "chars": 2293,
    "preview": "from logging import INFO as INFO_LEVEL\nfrom logging import FileHandler, Formatter, getLogger\nfrom pathlib import Path\nfr"
  },
  {
    "path": "src/storage/__init__.py",
    "chars": 64,
    "preview": "from .manager import RecordManager\n\n__all__ = [\"RecordManager\"]\n"
  },
  {
    "path": "src/storage/csv.py",
    "chars": 1492,
    "preview": "from csv import writer\nfrom os.path import getsize\nfrom pathlib import Path\nfrom platform import system\nfrom typing impo"
  },
  {
    "path": "src/storage/manager.py",
    "chars": 12662,
    "preview": "from shutil import move\nfrom typing import TYPE_CHECKING\n\nfrom .csv import CSVLogger\nfrom .sqlite import SQLLogger\nfrom "
  },
  {
    "path": "src/storage/mysql.py",
    "chars": 103,
    "preview": "from .sql import BaseSQLLogger\n\n__all__ = [\"MySQLLogger\"]\n\n\nclass MySQLLogger(BaseSQLLogger):\n    pass\n"
  },
  {
    "path": "src/storage/sql.py",
    "chars": 361,
    "preview": "from re import Pattern\nfrom re import compile\n\nfrom .text import BaseTextLogger\n\n__all__ = [\"BaseSQLLogger\"]\n\n\nclass Bas"
  },
  {
    "path": "src/storage/sqlite.py",
    "chars": 3513,
    "preview": "from pathlib import Path\nfrom re import sub\n\nfrom aiosqlite import connect\nfrom sqlite3 import OperationalError\n\nfrom ri"
  },
  {
    "path": "src/storage/text.py",
    "chars": 1825,
    "preview": "from pathlib import Path\nfrom typing import TYPE_CHECKING\nfrom typing import Union\n\nfrom ..tools import Retry\n\nif TYPE_C"
  },
  {
    "path": "src/storage/xlsx.py",
    "chars": 1768,
    "preview": "from pathlib import Path\nfrom typing import TYPE_CHECKING\n\nfrom openpyxl import Workbook, load_workbook\nfrom openpyxl.ut"
  },
  {
    "path": "src/testers/__init__.py",
    "chars": 54,
    "preview": "from .logger import Logger\nfrom .params import Params\n"
  },
  {
    "path": "src/testers/logger.py",
    "chars": 414,
    "preview": "class Logger:\n    @staticmethod\n    def info(\n        *args,\n    ):\n        print(\n            *args,\n        )\n\n    @st"
  },
  {
    "path": "src/testers/params.py",
    "chars": 3054,
    "preview": "from configparser import ConfigParser, NoOptionError, NoSectionError\n\nfrom rich.console import Console\n\nfrom src.custom "
  },
  {
    "path": "src/testers/test_format.py",
    "chars": 2077,
    "preview": "from http.cookiejar import Cookie, CookieJar\n\nfrom pytest import mark\n\nfrom src.tools import (\n    cookie_dict_to_str,\n "
  },
  {
    "path": "src/testers/translate.py",
    "chars": 298,
    "preview": "from src.translation import _, switch_language\nfrom src.custom import DISCLAIMER_TEXT\n\nif __name__ == \"__main__\":\n    pr"
  },
  {
    "path": "src/tools/__init__.py",
    "chars": 906,
    "preview": "from .browser import Browser\nfrom .capture import capture_error_params\nfrom .capture import capture_error_request\nfrom ."
  },
  {
    "path": "src/tools/browser.py",
    "chars": 4480,
    "preview": "from contextlib import suppress\nfrom sys import platform\nfrom types import SimpleNamespace\nfrom typing import TYPE_CHECK"
  },
  {
    "path": "src/tools/capture.py",
    "chars": 1908,
    "preview": "from json.decoder import JSONDecodeError\nfrom ssl import SSLError\nfrom typing import TYPE_CHECKING, Union\n\nfrom httpx im"
  },
  {
    "path": "src/tools/choose.py",
    "chars": 516,
    "preview": "from typing import TYPE_CHECKING, Union\n\nif TYPE_CHECKING:\n    from rich.console import Console\n\n    from src.tools impo"
  },
  {
    "path": "src/tools/cleaner.py",
    "chars": 2609,
    "preview": "from platform import system\nfrom re import compile\nfrom string import whitespace\n\nfrom emoji import replace_emoji\n\ntry:\n"
  },
  {
    "path": "src/tools/console.py",
    "chars": 1268,
    "preview": "from rich.console import Console\nfrom rich.text import Text\n\nfrom src.custom import (\n    PROMPT,\n    GENERAL,\n    INFO,"
  },
  {
    "path": "src/tools/error.py",
    "chars": 475,
    "preview": "from ..translation import _\n\n\nclass DownloaderError(Exception):\n    def __init__(\n        self,\n        message: str = \""
  },
  {
    "path": "src/tools/file_folder.py",
    "chars": 564,
    "preview": "from contextlib import suppress\nfrom pathlib import Path\n\n\ndef file_switch(path: Path) -> None:\n    if path.exists():\n  "
  },
  {
    "path": "src/tools/format.py",
    "chars": 1301,
    "preview": "from http.cookiejar import CookieJar\nfrom re import compile\n\n\ndef cookie_str_to_dict(cookie_str: str) -> dict:\n    if no"
  },
  {
    "path": "src/tools/list_pop.py",
    "chars": 91,
    "preview": "__all__ = [\"safe_pop\"]\n\n\ndef safe_pop(data: list):\n    return data.pop() if data else None\n"
  },
  {
    "path": "src/tools/progress.py",
    "chars": 600,
    "preview": "class FakeProgress:\n    def __init__(\n        self,\n        *args,\n        **kwargs,\n    ):\n        pass\n\n    async def "
  },
  {
    "path": "src/tools/rename_compatible.py",
    "chars": 414,
    "preview": "from ..custom import PROJECT_ROOT\nfrom shutil import copy2\n\n\nclass RenameCompatible:\n    OLD_DB_FILE = PROJECT_ROOT.join"
  },
  {
    "path": "src/tools/retry.py",
    "chars": 1889,
    "preview": "from ..custom import RETRY, wait\nfrom ..translation import _\n\n__all__ = [\"Retry\"]\n\n\nclass Retry:\n    \"\"\"重试器,仅适用于本项目!\"\"\"\n"
  },
  {
    "path": "src/tools/session.py",
    "chars": 2664,
    "preview": "from typing import TYPE_CHECKING, Union\n\nfrom httpx import AsyncClient, AsyncHTTPTransport, Client, HTTPTransport\n\nfrom "
  },
  {
    "path": "src/tools/temporary.py",
    "chars": 421,
    "preview": "from random import choice\nfrom string import (\n    ascii_lowercase,\n    ascii_uppercase,\n    digits,\n)\nfrom time import "
  },
  {
    "path": "src/tools/timer.py",
    "chars": 283,
    "preview": "from time import time\n\n__all__ = [\"run_time\"]\n\n\ndef run_time(function):\n    def inner(self, *args, **kwargs):\n        st"
  },
  {
    "path": "src/tools/truncate.py",
    "chars": 845,
    "preview": "from unicodedata import name\n\n\ndef is_chinese_char(char: str) -> bool:\n    return \"CJK\" in name(char, \"\")\n\n\ndef truncate"
  },
  {
    "path": "src/translation/__init__.py",
    "chars": 42,
    "preview": "from .translate import switch_language, _\n"
  },
  {
    "path": "src/translation/static.py",
    "chars": 343,
    "preview": "TRANSLATE_MAP = {\n    \"发布作品\": \"Posts\",\n    \"喜欢作品\": \"Liked\",\n    \"收藏作品\": \"Favorites\",\n    \"收藏夹\": \"Collections\",\n    \"收藏夹作"
  },
  {
    "path": "src/translation/translate.py",
    "chars": 2289,
    "preview": "from gettext import translation\nfrom locale import getlocale\nfrom pathlib import Path\n\nROOT = Path(__file__).resolve().p"
  },
  {
    "path": "src/tui_edition/__init__.py",
    "chars": 40,
    "preview": "from .app import App\n\n__all__ = [\"App\"]\n"
  },
  {
    "path": "src/tui_edition/app.py",
    "chars": 40,
    "preview": "__all__ = [\"App\"]\n\n\nclass App:\n    pass\n"
  },
  {
    "path": "src/tui_edition/setting.py",
    "chars": 48,
    "preview": "__all__ = [\"Setting\"]\n\n\nclass Setting:\n    pass\n"
  },
  {
    "path": "static/js/X-Bogus.js",
    "chars": 50780,
    "preview": "var window = null;\n\nfunction _0x5cd844(e) {\n    var b = {\n        exports: {}\n    };\n    return e(b, b.exports), b.expor"
  },
  {
    "path": "static/js/a_bogus.js",
    "chars": 15588,
    "preview": "// All the content in this article is only for learning and communication use, not for any other purpose, strictly prohi"
  }
]

// ... and 3 more files (download for full content)

About this extraction

This page contains the full source code of the JoeanAmier/TikTokDownloader GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 147 files (914.1 KB), approximately 274.2k tokens, and a symbol index with 1012 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!