Skip to content

fix: preprocessing_data crash for favorite/collection API mode - #764

Open
WenhuaXia wants to merge 2 commits into
JoeanAmier:masterfrom
WenhuaXia:fix/favorite-collection-preprocessing
Open

fix: preprocessing_data crash for favorite/collection API mode#764
WenhuaXia wants to merge 2 commits into
JoeanAmier:masterfrom
WenhuaXia:fix/favorite-collection-preprocessing

Conversation

@WenhuaXia

@WenhuaXia WenhuaXia commented Jun 15, 2026

Copy link
Copy Markdown

Bug: When calling `/douyin/account` API with `tab=favorite` or `tab=collection`, the endpoint crashes with `DownloaderError: 提取账号信息或合集信息失败`.

Root Cause:

  1. API mode skips user info lookup (`info = None`), causing `preprocessing_data` to fall through to the `list` branch which tries to match `sec_uid` from video data.
  2. For `favorite`/collection modes, video authors are original creators, not the target user — `sec_uid` never matches.

Fix:

  • `extractor.py`: Split `post` from `favorite`/collection in `preprocessing_data`. Add try/except fallback so when video data can't be matched, gracefully return `user_id` + `mark`.
  • `main_terminal.py`: `favorite`/collection modes always query user info even in API mode, ensuring accurate metadata.

Summary by Sourcery

在预处理和 API 流程中正确处理“收藏”和“合集”账号模式,以避免崩溃并确保用户元数据可用。

Bug 修复:

  • 通过添加专门的预处理逻辑,并在无法匹配视频数据时提供安全回退,防止在处理“收藏”或“合集”标签页时发生崩溃。
  • 确保在 API 模式下,“收藏”和“合集”标签页仍然会获取用户信息,从而保持账号元数据的准确性。
Original summary in English

Summary by Sourcery

Handle favorite and collection account modes correctly in preprocessing and API flows to avoid crashes and ensure user metadata is available.

Bug Fixes:

  • Prevent crashes when processing favorite or collection tabs by adding dedicated preprocessing logic and a safe fallback when video data cannot be matched.
  • Ensure favorite and collection tabs in API mode still fetch user info so account metadata remains accurate.

@sourcery-ai

sourcery-ai Bot commented Jun 15, 2026

Copy link
Copy Markdown

审阅者指南

调整抖音账号预处理逻辑,以在 API 模式下正确处理“喜欢/收藏”标签页:将其逻辑与普通作品处理分离,并确保会拉取用户信息;当基于 sec_uid 的匹配失败时,能够优雅地降级处理。

API 模式下 deal_account_detail 获取用户信息的时序图

sequenceDiagram
    actor Client
    participant MainTerminal
    participant UserInfoService

    Client->>MainTerminal: deal_account_detail(api, tab)
    alt [api and tab not in {favorite, collection}]
        MainTerminal->>MainTerminal: info = None
    else [not api or tab in {favorite, collection}]
        MainTerminal->>UserInfoService: get_user_info_data(user_id)
        UserInfoService-->>MainTerminal: info
    end
    MainTerminal-->>Client: processed account detail
Loading

preprocessing_data 处理“喜欢/收藏”标签页的流程图

flowchart TD
    A[preprocessing_data called] --> B{mode}
    B -->|favorite or collection| C[try __select_item with sec_uid]
    C --> D[__extract_pretreatment_data with uid and nickname]
    D --> E[return id_, name, mark]
    C -->|DownloaderError| F[cleaner.filter_name mark user_id]
    F --> G[return user_id, filtered_name, filtered_name]
    B -->|other modes| H[existing processing paths]
Loading

文件级改动

变更 详情 文件
为“喜欢/收藏”标签页单独拆分预处理路径,并在基于 sec_uid 的选择失败时提供健壮的回退方案。
  • 扩展 preprocessing_data 中的 match-case,新增“favorite”和“collection”标签页的专用分支,而不是继续共用列表/作品的逻辑。
  • 在“favorite/collection”分支中,尝试使用提供数据中的 sec_uid 选择一个条目,并提取预处理数据(uidnicknamemark)。
  • 将“favorite/collection”的提取逻辑包裹在 try/except DownloaderError 中,当失败时,不再抛出异常,而是返回 user_id 以及基于 cleaner.filter_name 推导出的 name/mark
src/extract/extractor.py
确保在 API 模式下,“喜欢/收藏”标签页仍会获取用户信息,以便为预处理提供元数据。
  • 修改 deal_account_detail 中控制“是否获取用户信息”的条件,使 API 模式仅在标签页不是 favorite 或 collection 时才跳过用户信息获取。
  • 允许 API 模式下的“favorite/collection”流程在预处理前获得准确的用户元数据。
src/application/main_terminal.py

提示与命令

与 Sourcery 交互

  • 触发新的审查: 在 Pull Request 中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的审查评论。
  • 从审查评论生成 GitHub Issue: 回复 Sourcery 的审查评论,请求其基于该评论创建 Issue。你也可以直接回复该评论 @sourcery-ai issue 来从中创建 Issue。
  • 生成 Pull Request 标题: 在 Pull Request 标题的任意位置写上 @sourcery-ai,即可随时生成标题。你也可以在 Pull Request 中评论 @sourcery-ai title 来(重新)生成标题。
  • 生成 Pull Request 概要: 在 Pull Request 正文任意位置写上 @sourcery-ai summary,即可在对应位置生成 PR 概要。你也可以在 Pull Request 中评论 @sourcery-ai summary 来在任意时间(重新)生成概要。
  • 生成审阅者指南: 在 Pull Request 中评论 @sourcery-ai guide,即可在任意时间(重新)生成审阅者指南。
  • 一次性解决所有 Sourcery 评论: 在 Pull Request 中评论 @sourcery-ai resolve,即可标记解决所有 Sourcery 评论。如果你已经处理了所有评论且不想再看到它们,这会很有用。
  • 一次性忽略所有 Sourcery 审查: 在 Pull Request 中评论 @sourcery-ai dismiss,即可忽略所有现有的 Sourcery 审查。特别适合当你希望从一次全新的审查开始——别忘了再评论 @sourcery-ai review 触发新的审查!

自定义你的使用体验

打开你的 控制面板 以:

  • 启用或停用审查功能,比如 Sourcery 自动生成的 Pull Request 概要、审阅者指南等。
  • 更改审查语言。
  • 添加、删除或编辑自定义审查说明。
  • 调整其他审查相关设置。

获取帮助

Original review guide in English

Reviewer's Guide

Adjusts Douyin account preprocessing to correctly handle favorite/collection tabs in API mode by separating their logic from normal post handling and ensuring user info is fetched, with a graceful fallback when sec_uid-based matching fails.

Sequence diagram for deal_account_detail user info fetching in API mode

sequenceDiagram
    actor Client
    participant MainTerminal
    participant UserInfoService

    Client->>MainTerminal: deal_account_detail(api, tab)
    alt [api and tab not in {favorite, collection}]
        MainTerminal->>MainTerminal: info = None
    else [not api or tab in {favorite, collection}]
        MainTerminal->>UserInfoService: get_user_info_data(user_id)
        UserInfoService-->>MainTerminal: info
    end
    MainTerminal-->>Client: processed account detail
Loading

Flow diagram for preprocessing_data handling favorite/collection tabs

flowchart TD
    A[preprocessing_data called] --> B{mode}
    B -->|favorite or collection| C[try __select_item with sec_uid]
    C --> D[__extract_pretreatment_data with uid and nickname]
    D --> E[return id_, name, mark]
    C -->|DownloaderError| F[cleaner.filter_name mark user_id]
    F --> G[return user_id, filtered_name, filtered_name]
    B -->|other modes| H[existing processing paths]
Loading

File-Level Changes

Change Details Files
Separate preprocessing path for favorite/collection tabs with a robust fallback when sec_uid-based selection fails.
  • Extend the preprocessing_data match-case to add a dedicated branch for favorite and collection tabs instead of sharing the list/post logic.
  • In the favorite/collection branch, attempt to select an item using sec_uid from the provided data and extract pretreatment data (uid, nickname, mark).
  • Wrap the favorite/collection extraction in a try/except DownloaderError and, on failure, return user_id plus a name/mark derived from cleaner.filter_name instead of raising.
src/extract/extractor.py
Ensure user info is still fetched in API mode for favorite/collection tabs so metadata is available for preprocessing.
  • Change the condition that disables user info fetching in deal_account_detail so that API mode skips user info only when the tab is not favorite or collection.
  • Allow favorite/collection flows in API mode to obtain accurate user metadata before preprocessing.
src/application/main_terminal.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 1 个问题,并且留下了一些整体性的反馈:

  • 在新的 case "favorite" | "collection" 分支中,多次重复使用 (self.extract_params_tiktok if tiktok else self.extract_params)[...] 查找会让代码更难阅读和维护;可以考虑先把选择好的参数字典赋值给一个局部变量,然后重复使用它。
  • except DownloaderError: 代码块会悄悄吞掉所有下载器错误,并通过返回一个兜底元组改变原有行为;如果可能的话,尽量收窄异常捕获范围,或者至少记录/追踪原始错误以便调试。
  • favorite/collection 的兜底返回中,namemark 字段都来源于 self.cleaner.filter_name(mark, user_id);请再次确认这两个输出是否确实应该完全相同,还是说 mark 应该保留不同的语义信息。
面向 AI Agent 的提示
Please address the comments from this code review:

## Overall Comments
- In the new `case "favorite" | "collection"` branch, the repeated `(self.extract_params_tiktok if tiktok else self.extract_params)[...]` lookups make the code harder to read and maintain; consider assigning the chosen params dict to a local variable and reusing it.
- The `except DownloaderError:` block silently swallows all downloader errors and changes behavior by returning a fallback tuple; if possible, narrow the exception scope or at least log/trace the original error to aid debugging.
- In the fallback return for `favorite`/`collection`, both the `name` and `mark` fields are derived from `self.cleaner.filter_name(mark, user_id)`; double-check whether these two outputs are intended to be identical or if `mark` should preserve a different semantic value.

## Individual Comments

### Comment 1
<location path="src/extract/extractor.py" line_range="898" />
<code_context>
                         mark,
                     )
                     return id_, name, mark
+                case "favorite" | "collection":
+                    try:
+                        item = self.__select_item(
</code_context>
<issue_to_address>
**issue (complexity):** 考虑引入一个局部的 params 变量,并在需要时再抽取一个辅助方法,以避免在新的 favorite/collection 分支中重复相同的三元表达式和参数列表。

你可以通过引入本地 `params` 变量并“拉平”调用结构,来减少这个新分支中的重复代码和嵌套。这能在保持行为不变的前提下,让代码更容易阅读和维护。

例如,将下面的代码:

```python
case "favorite" | "collection":
    try:
        item = self.__select_item(
            data,
            user_id,
            (self.extract_params_tiktok if tiktok else self.extract_params)[
                "sec_uid"
            ],
        )
        id_, name, mark = self.__extract_pretreatment_data(
            item,
            (self.extract_params_tiktok if tiktok else self.extract_params)[
                "uid"
            ],
            (self.extract_params_tiktok if tiktok else self.extract_params)[
                "nickname"
            ],
            mark,
        )
        return id_, name, mark
    except DownloaderError:
        return (
            user_id,
            self.cleaner.filter_name(mark, user_id),
            self.cleaner.filter_name(mark, user_id),
        )
```

重构为:

```python
case "favorite" | "collection":
    params = self.extract_params_tiktok if tiktok else self.extract_params

    try:
        item = self.__select_item(
            data,
            user_id,
            params["sec_uid"],
        )
        id_, name, mark = self.__extract_pretreatment_data(
            item,
            params["uid"],
            params["nickname"],
            mark,
        )
        return id_, name, mark
    except DownloaderError:
        cleaned = self.cleaner.filter_name(mark, user_id)
        return user_id, cleaned, cleaned
```

如果在其他分支中也存在类似模式,你还可以抽取一个小的辅助方法,进一步统一流程:

```python
def _pretreat_user_item(self, data, user_id, params, mark):
    item = self.__select_item(data, user_id, params["sec_uid"])
    return self.__extract_pretreatment_data(
        item,
        params["uid"],
        params["nickname"],
        mark,
    )
```

然后在分支中这么写:

```python
case "favorite" | "collection":
    params = self.extract_params_tiktok if tiktok else self.extract_params

    try:
        id_, name, mark = self._pretreat_user_item(data, user_id, params, mark)
        return id_, name, mark
    except DownloaderError:
        cleaned = self.cleaner.filter_name(mark, user_id)
        return user_id, cleaned, cleaned
```
</issue_to_address>

Sourcery 对开源项目是免费的 —— 如果你觉得我们的评审有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈来改进后续的评审。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • In the new case "favorite" | "collection" branch, the repeated (self.extract_params_tiktok if tiktok else self.extract_params)[...] lookups make the code harder to read and maintain; consider assigning the chosen params dict to a local variable and reusing it.
  • The except DownloaderError: block silently swallows all downloader errors and changes behavior by returning a fallback tuple; if possible, narrow the exception scope or at least log/trace the original error to aid debugging.
  • In the fallback return for favorite/collection, both the name and mark fields are derived from self.cleaner.filter_name(mark, user_id); double-check whether these two outputs are intended to be identical or if mark should preserve a different semantic value.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In the new `case "favorite" | "collection"` branch, the repeated `(self.extract_params_tiktok if tiktok else self.extract_params)[...]` lookups make the code harder to read and maintain; consider assigning the chosen params dict to a local variable and reusing it.
- The `except DownloaderError:` block silently swallows all downloader errors and changes behavior by returning a fallback tuple; if possible, narrow the exception scope or at least log/trace the original error to aid debugging.
- In the fallback return for `favorite`/`collection`, both the `name` and `mark` fields are derived from `self.cleaner.filter_name(mark, user_id)`; double-check whether these two outputs are intended to be identical or if `mark` should preserve a different semantic value.

## Individual Comments

### Comment 1
<location path="src/extract/extractor.py" line_range="898" />
<code_context>
                         mark,
                     )
                     return id_, name, mark
+                case "favorite" | "collection":
+                    try:
+                        item = self.__select_item(
</code_context>
<issue_to_address>
**issue (complexity):** Consider introducing a local params variable and possibly a helper method to avoid repeating the same ternary and argument lists in the new favorite/collection case branch.

You can reduce duplication and nesting in the new branch by introducing a local `params` variable and flattening the calls. This keeps behavior identical while making the code easier to scan and maintain.

For example, refactor this:

```python
case "favorite" | "collection":
    try:
        item = self.__select_item(
            data,
            user_id,
            (self.extract_params_tiktok if tiktok else self.extract_params)[
                "sec_uid"
            ],
        )
        id_, name, mark = self.__extract_pretreatment_data(
            item,
            (self.extract_params_tiktok if tiktok else self.extract_params)[
                "uid"
            ],
            (self.extract_params_tiktok if tiktok else self.extract_params)[
                "nickname"
            ],
            mark,
        )
        return id_, name, mark
    except DownloaderError:
        return (
            user_id,
            self.cleaner.filter_name(mark, user_id),
            self.cleaner.filter_name(mark, user_id),
        )
```

into:

```python
case "favorite" | "collection":
    params = self.extract_params_tiktok if tiktok else self.extract_params

    try:
        item = self.__select_item(
            data,
            user_id,
            params["sec_uid"],
        )
        id_, name, mark = self.__extract_pretreatment_data(
            item,
            params["uid"],
            params["nickname"],
            mark,
        )
        return id_, name, mark
    except DownloaderError:
        cleaned = self.cleaner.filter_name(mark, user_id)
        return user_id, cleaned, cleaned
```

If similar patterns exist in other cases, you could also extract a small helper to further standardize the flow:

```python
def _pretreat_user_item(self, data, user_id, params, mark):
    item = self.__select_item(data, user_id, params["sec_uid"])
    return self.__extract_pretreatment_data(
        item,
        params["uid"],
        params["nickname"],
        mark,
    )
```

and then:

```python
case "favorite" | "collection":
    params = self.extract_params_tiktok if tiktok else self.extract_params

    try:
        id_, name, mark = self._pretreat_user_item(data, user_id, params, mark)
        return id_, name, mark
    except DownloaderError:
        cleaned = self.cleaner.filter_name(mark, user_id)
        return user_id, cleaned, cleaned
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/extract/extractor.py
- Extract repeated conditional expression into `params` variable
- Add warning log when falling back to cleaned user info
- Fix mark fallback to use filtered name as default instead of user_id

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

⚠️ 此 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.

@github-actions github-actions Bot added the 未跟进问题(Stale) 长期未更新的议题 label Jul 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

未跟进问题(Stale) 长期未更新的议题

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant