diff --git a/app/modules/imdb/api.py b/app/modules/imdb/api.py index 85e64aa68..215fa6716 100644 --- a/app/modules/imdb/api.py +++ b/app/modules/imdb/api.py @@ -415,27 +415,31 @@ class ImdbApi: item = cls._first_title(data) if not item: return None - genres = item.get("titleGenres", {}).get("genres") or [] - countries = item.get("countriesOfOrigin", {}).get("countries") or [] - languages = item.get("spokenLanguages", {}).get("spokenLanguages") or [] + genres = (item.get("titleGenres") or {}).get("genres") or [] + countries = (item.get("countriesOfOrigin") or {}).get("countries") or [] + languages = (item.get("spokenLanguages") or {}).get("spokenLanguages") or [] return cls._parse( ImdbTitle, { "id": item.get("id"), - "type": item.get("titleType", {}).get("id"), + "type": (item.get("titleType") or {}).get("id"), "isAdult": item.get("isAdult"), - "primaryTitle": item.get("titleText", {}).get("text"), - "originalTitle": item.get("originalTitleText", {}).get("text"), + "primaryTitle": (item.get("titleText") or {}).get("text"), + "originalTitle": (item.get("originalTitleText") or {}).get( + "text" + ), "primaryImage": item.get("primaryImage"), - "startYear": item.get("releaseYear", {}).get("year"), - "runtimeSeconds": item.get("runtime", {}).get("seconds"), + "startYear": (item.get("releaseYear") or {}).get("year"), + "runtimeSeconds": (item.get("runtime") or {}).get("seconds"), "genres": [ - entry.get("genre", {}).get("text") + (entry.get("genre") or {}).get("text") for entry in genres - if entry.get("genre", {}).get("text") + if (entry.get("genre") or {}).get("text") ], "rating": item.get("ratingsSummary"), - "plot": item.get("plot", {}).get("plotText", {}).get("plainText"), + "plot": ((item.get("plot") or {}).get("plotText") or {}).get( + "plainText" + ), "originCountries": [ {"code": entry.get("id"), "name": entry.get("text")} for entry in countries diff --git a/tests/test_imdb_module.py b/tests/test_imdb_module.py index 0466a29f9..615852710 100644 --- a/tests/test_imdb_module.py +++ b/tests/test_imdb_module.py @@ -169,6 +169,39 @@ def test_imdb_graphql_error_is_not_cached() -> None: assert api._request.post_json.call_count == 2 +def test_imdb_title_parser_accepts_null_optional_objects() -> None: + """GraphQL 可选对象为 null 时应保留可用详情并归一为空值。""" + detail = ImdbApi._parse_title( + { + "titles": [ + { + "id": "tt30836097", + "titleType": {"id": "tvSeries"}, + "titleText": {"text": "19th Floor"}, + "originalTitleText": None, + "releaseYear": None, + "runtime": None, + "titleGenres": None, + "countriesOfOrigin": None, + "spokenLanguages": None, + "plot": None, + } + ] + } + ) + + assert detail is not None + assert detail.id == "tt30836097" + assert detail.primary_title == "19th Floor" + assert detail.original_title is None + assert detail.start_year is None + assert detail.runtime_seconds is None + assert detail.genres == [] + assert detail.origin_countries == [] + assert detail.spoken_languages == [] + assert detail.plot is None + + def test_imdb_clear_cache_registers_async_cleanup_in_running_loop() -> None: """同步清缓存入口在异步宿主中应登记任务,并保留原同步调用约定。"""