openapi: 3.0.3
info:
  title: 无痕AI 视频处理 API
  version: 2.1.0
  description: |-
    无痕AI API 由杭州岁羽网络科技有限公司开发与维护，通过 API 形式输出视频去水印/去字幕、
    万能视频消除和视频增强等 AI 处理能力。

    ## 调用流程
    1. 使用 API Key 调用 `GET /user/access_token` 换取 access_token（有效期 7 天，过期后返回 401，需重新换取）。
    2. 将 access_token 以 `Authorization: Bearer <access_token>` 形式放入请求头，调用任务接口。
    3. 任务异步处理：通过回调（需先 `POST /user/notify_callback` 注册）或 `GET /status` / 定时查询主动获取进度。
    4. 处理完成后，GPU 服务器通过预签名 `upload_url` 上传处理结果。

    ## 通用约定
    - 所有接口（含 access_token）都必须携带 query 参数 `nonce`（随机字符串）与 `t`（当前 UTC 秒数时间戳），否则返回失败。
    - 除 `GET /user/access_token` 外，所有请求都必须携带请求头 `Authorization: Bearer <access_token>`（缺失、无效或过期时返回 HTTP 401）。
    - 所有响应统一为 `{ code, message, data }` 结构，`code = 0` 表示成功，非 0 表示失败。
    - API 仅负责处理、不负责存储：请自备云存储（阿里云 OSS / 腾讯云 COS / 七牛云等）并生成预签名上传 URL（upload_url），处理完成后由 GPU 服务器 PUT 上传结果。

    ## 任务状态（status）
    | 状态 | 含义 |
    |---|---|
    | created | 任务已创建，未启动 |
    | queued | 已启动，排队等候处理 |
    | processing | 处理中（下载 10% + 处理 80% + 上传 10%） |
    | success | 处理完成并上传成功 |
    | failed | 处理失败 |
    | paused | 已取消（通过 cancel 接口） |

    ## 异常 code
    | code | 名称 | 说明 |
    |---|---|---|
    | 0 | 成功 | 请求处理成功 |
    | 1001 | 参数错误 | 缺少必填参数、参数格式不正确或访问了无效路径 |
    | 3001 | 积分不足 | 当前账号积分余额不足，无法创建任务 |
    | 4001 | 资源错误 | 常见于工作流配置不存在 |
    | 4003 | 用户不可用 | 用户不存在、已禁用或状态不可用 |
    | 4004 | 任务不存在 | task_id 不存在，或不属于当前用户 |
    | 4005 | 任务不可取消 | 仅排队中的任务支持取消 |
    | 401 | 未登录 | access_token 缺失、无效或已过期 |
    | 7003 | API Key 无效或已禁用 | API Key 不存在、已禁用或不可用 |
    | 7004 | API Key 已过期 | 已超过有效期，无法换取 access_token |
servers:
  - url: https://api.wuhenai.com/v2
    description: 生产环境
tags:
  - name: User
    description: 账号相关操作（token、积分、账单、回调注册）
  - name: Task
    description: 视频/图片处理任务的创建与管理
security:
  - AccessToken: []
paths:
  /user/access_token:
    get:
      tags: [User]
      operationId: getAccessToken
      summary: 获取 Access Token
      description: 使用 API Key 换取 access_token（有效期 7 天）。除本接口外，其他接口都必须携带 access_token。
      security: []
      parameters:
        - $ref: '#/components/parameters/Nonce'
        - $ref: '#/components/parameters/Timestamp'
        - name: api_key
          in: query
          required: true
          description: 对应账号的 API Key，由客服通过官方邮件发送，请妥善保管。
          schema:
            type: string
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                type: object
                required: [code, message, data]
                properties:
                  code:
                    type: integer
                    description: 状态码，0 表示成功
                  message:
                    type: string
                    description: 成功或失败原因
                  data:
                    $ref: '#/components/schemas/AccessTokenData'
  /user/me:
    get:
      tags: [User]
      operationId: getAccountInfo
      summary: 获取账户信息
      description: 获取当前账户的可用积分余额。
      parameters:
        - $ref: '#/components/parameters/Nonce'
        - $ref: '#/components/parameters/Timestamp'
      responses:
        '401':
          $ref: '#/components/responses/Unauthorized'
        '200':
          description: 成功
          content:
            application/json:
              schema:
                type: object
                required: [code, message, data]
                properties:
                  code:
                    type: integer
                  message:
                    type: string
                  data:
                    $ref: '#/components/schemas/AccountData'
  /user/notify_callback:
    post:
      tags: [User]
      operationId: registerNotifyCallback
      summary: 注册通知回调
      description: |-
        注册任务处理进度与结果的通知回调地址。注册后，任务状态变化会以 POST 请求发送到 callback_url，
        请求体结构见 components/schemas/CallbackPayload。
      parameters:
        - $ref: '#/components/parameters/Nonce'
        - $ref: '#/components/parameters/Timestamp'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [callback_url]
              properties:
                callback_url:
                  type: string
                  format: uri
                  description: 回调地址。
      responses:
        '401':
          $ref: '#/components/responses/Unauthorized'
        '200':
          description: 成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmptyResponse'
  /user/billings:
    get:
      tags: [User]
      operationId: getBillings
      summary: 查询账单
      description: 查询近 90 天内的充值与消费账单。超过 30 天的账单需通过客服人工导出。
      parameters:
        - $ref: '#/components/parameters/Nonce'
        - $ref: '#/components/parameters/Timestamp'
        - name: time_start
          in: query
          required: false
          description: 账单查询开始时间（UTC 秒）。
          schema:
            type: integer
            format: int64
        - name: time_stop
          in: query
          required: false
          description: 账单查询结束时间（UTC 秒）。
          schema:
            type: integer
            format: int64
        - name: page_index
          in: query
          required: false
          description: 分页索引，默认 0。
          schema:
            type: integer
            default: 0
      responses:
        '401':
          $ref: '#/components/responses/Unauthorized'
        '200':
          description: 成功
          content:
            application/json:
              schema:
                type: object
                required: [code, message, data]
                properties:
                  code:
                    type: integer
                  message:
                    type: string
                  data:
                    $ref: '#/components/schemas/BillingsData'
  /video_removal:
    post:
      tags: [Task]
      operationId: createVideoRemoval
      summary: 视频去水印/去字幕
      description: 消除视频中通过后期包装添加的字幕、图形水印、LOGO 水印、文字水印、移动水印等。
      parameters:
        - $ref: '#/components/parameters/Nonce'
        - $ref: '#/components/parameters/Timestamp'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VideoRemovalRequest'
      responses:
        '401':
          $ref: '#/components/responses/Unauthorized'
        '200':
          description: 成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskCreatedResponse'
  /video_eraser:
    post:
      tags: [Task]
      operationId: createVideoEraser
      summary: 视频消除
      description: 消除视频中的原生内容（如行走的人、移动的汽车），也可消除马赛克等遮挡，修复画面。
      parameters:
        - $ref: '#/components/parameters/Nonce'
        - $ref: '#/components/parameters/Timestamp'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VideoEraserRequest'
      responses:
        '401':
          $ref: '#/components/responses/Unauthorized'
        '200':
          description: 成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskCreatedResponse'
  /video_upscale:
    post:
      tags: [Task]
      operationId: createVideoUpscale
      summary: 视频增强
      description: 通过 AI 增强提升视频分辨率与画面细节，转为 1080P 高清甚至 2K 超清。
      parameters:
        - $ref: '#/components/parameters/Nonce'
        - $ref: '#/components/parameters/Timestamp'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VideoUpscaleRequest'
      responses:
        '401':
          $ref: '#/components/responses/Unauthorized'
        '200':
          description: 成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskCreatedResponse'
  /photo_eraser:
    post:
      tags: [Task]
      operationId: createPhotoEraser
      summary: 图片消除（暂未开放）
      description: |-
        消除图片中的水印、文字及不想要的画面元素（如眼镜、脸上的痣等）。
        **暂未上线。** 注：原文档定义为 GET /photo_eraser，因携带请求体、语义为创建任务，规范中已改为 POST。
      deprecated: true
      parameters:
        - $ref: '#/components/parameters/Nonce'
        - $ref: '#/components/parameters/Timestamp'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PhotoEraserRequest'
      responses:
        '401':
          $ref: '#/components/responses/Unauthorized'
        '200':
          description: 成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskCreatedResponse'
  /cancel:
    post:
      tags: [Task]
      operationId: cancelTask
      summary: 取消任务
      description: 取消排队中的任务。任务已开始处理或已完成则返回失败。取消的任务不扣积分。
      parameters:
        - $ref: '#/components/parameters/Nonce'
        - $ref: '#/components/parameters/Timestamp'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [task_id]
              properties:
                task_id:
                  type: string
                  description: 需要取消的任务 ID。
      responses:
        '401':
          $ref: '#/components/responses/Unauthorized'
        '200':
          description: 成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmptyResponse'
components:
  responses:
    Unauthorized:
      description: 未登录（access_token 缺失、无效或已过期）
      content:
        application/json:
          schema:
            type: object
            required: [code, message, data]
            properties:
              code:
                type: integer
                example: 401
              message:
                type: string
              data:
                type: object
  securitySchemes:
    AccessToken:
      type: http
      scheme: bearer
      description: '携带 access_token 的 Bearer 认证，格式为 `Authorization: Bearer <access_token>`。'
  parameters:
    Nonce:
      name: nonce
      in: query
      required: true
      description: 随机字符串，每次请求唯一，不参与签名。
      schema:
        type: string
    Timestamp:
      name: t
      in: query
      required: true
      description: 当前 UTC 时间（自 1970/1/1 起的秒数）。
      schema:
        type: integer
        format: int64
  schemas:
    EmptyResponse:
      type: object
      required: [code, message, data]
      properties:
        code:
          type: integer
          description: 状态码，0 表示成功
        message:
          type: string
        data:
          type: object
          description: 业务数据（此接口无额外内容）
    AccessTokenData:
      type: object
      required: [expired, access_token]
      properties:
        expired:
          type: integer
          format: int64
          description: access_token 过期的 UTC 时间戳（秒）。
        access_token:
          type: string
          description: 用于后续接口认证的 access token。
    AccountData:
      type: object
      required: [account_id, account_name, balance]
      properties:
        account_id:
          type: string
          description: 唯一账户 ID。
        account_name:
          type: string
          description: 账户名称。
        balance:
          type: integer
          description: 账户可用积分余额。
    BillingItem:
      type: object
      required: [billing_id, billing_type, credits, create_at]
      properties:
        billing_id:
          type: string
          description: 账单 ID。
        billing_type:
          type: string
          enum: [free, buy_credits, consume, refund]
          description: 账单类型。
        credits:
          type: integer
          description: 积分增减（负表示消耗，正表示增加）。
        task_id:
          type: string
          description: billing_type=consume 时对应的任务 ID。
        task_type:
          type: string
          enum: [removeWatermark_vid, eraseObject_vid, eraseObject_pic]
          description: 任务类型。
        model:
          type: string
          description: 使用的模型名。
        method:
          type: string
          description: 处理方式。
        metering:
          type: string
          description: 任务的计费依据。
        amount:
          type: integer
          description: 支付金额（单位：元；仅支付类账单有值，其余为空或 0）。
        create_at:
          type: integer
          format: int64
          description: 账单创建时间（UTC 秒）。
    BillingsData:
      type: object
      required: [total_pages, items]
      properties:
        total_pages:
          type: integer
          description: 总页数。
        items:
          type: array
          description: 符合条件的账单列表。
          items:
            $ref: '#/components/schemas/BillingItem'
    Rect:
      type: object
      description: 待处理区域（method=sel_area 时使用）。像素面积 width*height <= 480000。
      required: [x1, y1, x2, y2]
      properties:
        x1:
          type: integer
        y1:
          type: integer
        x2:
          type: integer
        y2:
          type: integer
    VideoRemovalRequest:
      type: object
      required: [video_url, upload_url]
      properties:
        video_url:
          type: string
          format: uri
          description: 待处理的视频 URL（过期时间尽量大，建议 >1 天）。
        model:
          type: string
          enum: [video_removal_std, video_removal_pro]
          default: video_removal_std
          description: 处理模型。
        method:
          type: string
          enum: [all_area, sel_area]
          default: all_area
          description: 处理方式。
        rect:
          $ref: '#/components/schemas/Rect'
        upload_url:
          type: string
          format: uri
          description: 预签名上传 URL（过期时间需 >24 小时）。
        upload_headers:
          type: object
          description: GPU PUT 上传时附带的 Headers。
          additionalProperties:
            type: string
    VideoEraserRequest:
      type: object
      required: [video_url, track_mode, upload_url]
      properties:
        video_url:
          type: string
          format: uri
          description: 待处理的视频 URL（可下载链接，非播放链接）。
        track_mode:
          type: string
          enum: [no_tracking, auto_tracking]
          description: 跟踪模式。
        time_mask:
          type: integer
          description: custom_mask 标记的时间点（毫秒），从视频开始位置计算。track_mode=auto_tracking 时需提供。
        custom_mask:
          type: string
          format: byte
          description: 标记待消除区域的 mask（base64）。track_mode=auto_tracking 时需提供。
        upload_url:
          type: string
          format: uri
          description: 预签名上传 URL（过期时间需 >24 小时）。
        upload_headers:
          type: object
          description: GPU PUT 上传时附带的 Headers。
          additionalProperties:
            type: string
    VideoUpscaleRequest:
      type: object
      required: [video_url, upscale_option, upload_url]
      properties:
        video_url:
          type: string
          format: uri
          description: 待处理的视频 URL（可下载链接，非播放链接）。
        upscale_option:
          type: string
          enum: [UPS_1080P, UPS_2K]
          description: 输出分辨率。
        upload_url:
          type: string
          format: uri
          description: 预签名上传 URL（过期时间需 >24 小时）。
        upload_headers:
          type: object
          description: GPU PUT 上传时附带的 Headers。
          additionalProperties:
            type: string
    PhotoEraserRequest:
      type: object
      required: [image_url, remove_option, upload_url]
      properties:
        image_url:
          type: string
          format: uri
          description: 待消除的原始照片公网 URL。
        remove_option:
          type: string
          enum: [PXC_1001, PXC_1002, PXC_1003, PXC_1004, PXC_1005, PXC_9000, PXC_0000]
          description: 消除目标预设 ID（各预设含义待补充）。
        custom_prompt:
          type: string
          description: 用户自定义提示词（PXC_9000 时使用）。
        custom_mask:
          type: string
          format: byte
          description: 用户自定义消除区 mask（PXC_0000 时使用，base64）。
        upload_url:
          type: string
          format: uri
          description: 预签名上传 URL（过期时间需 >24 小时）。
        upload_headers:
          type: object
          description: GPU PUT 上传时附带的 Headers。
          additionalProperties:
            type: string
    TaskCreatedResponse:
      type: object
      required: [code, message, data]
      properties:
        code:
          type: integer
          description: 状态码，0 表示成功
        message:
          type: string
        data:
          type: object
          required: [task_id]
          properties:
            task_id:
              type: string
              description: 当前任务 ID。
    CallbackPayload:
      type: object
      description: 任务回调的请求体结构（由无痕AI 以 POST 发送到已注册的 callback_url）。与所有接口一致，统一为 code/message/data 结构。
      required: [code, message, data]
      properties:
        code:
          type: integer
          description: 状态码，0 表示成功。
        message:
          type: string
          description: 处理消息。
        data:
          type: object
          required: [task_id, task_type, status, progress]
          properties:
            task_id:
              type: string
              description: 回调的任务 ID。
            task_type:
              type: string
              description: 当前任务类型。
            status:
              type: string
              enum: [created, queued, processing, success, failed, paused]
              description: 当前任务状态。
            progress:
              type: integer
              description: 任务进度（queued 时为前面排队任务数；processing 时为 0-100 整数）。
            metering:
              type: integer
              description: status=success 时计费依据（视频类：时长秒；图片类：张）。
            credits:
              type: integer
              description: status=success 时本次任务扣除的积分数。
            description:
              type: string
              description: status=failed 时错误原因。
