From 36cf26d892184c1436c02629e9e67de5cc3eeed6 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Sat, 17 Oct 2026 00:38:37 +0800 Subject: [PATCH] fix(tmdb): preserve search pagination when adult filter is false --- .../themoviedb/tmdbv3api/objs/search.py | 25 +++++++-------- tests/test_tmdb_search_parameters.py | 31 +++++++++++++++++++ 2 files changed, 43 insertions(+), 13 deletions(-) create mode 100644 tests/test_tmdb_search_parameters.py diff --git a/app/modules/themoviedb/tmdbv3api/objs/search.py b/app/modules/themoviedb/tmdbv3api/objs/search.py index 580924cac..1336d76dd 100644 --- a/app/modules/themoviedb/tmdbv3api/objs/search.py +++ b/app/modules/themoviedb/tmdbv3api/objs/search.py @@ -1,12 +1,11 @@ -from ..tmdb import TMDb +from urllib.parse import quote -try: - from urllib import quote -except ImportError: - from urllib.parse import quote +from ..tmdb import TMDb class Search(TMDb): + """提供电影、剧集、人物及其他实体的同步和异步搜索。""" + _urls = { "companies": "/search/company", "collections": "/search/collection", @@ -69,7 +68,7 @@ class Search(TMDb): """ params = "query=%s&page=%s" % (quote(term), page) if adult is not None: - params += "&include_adult=%s" % "true" if adult else "false" + params += "&include_adult=%s" % ("true" if adult else "false") if region is not None: params += "®ion=%s" % quote(region) if year is not None: @@ -94,7 +93,7 @@ class Search(TMDb): """ params = "query=%s&page=%s" % (quote(term), page) if adult is not None: - params += "&include_adult=%s" % "true" if adult else "false" + params += "&include_adult=%s" % ("true" if adult else "false") if region is not None: params += "®ion=%s" % quote(region) return self._request_obj( @@ -114,7 +113,7 @@ class Search(TMDb): """ params = "query=%s&page=%s" % (quote(term), page) if adult is not None: - params += "&include_adult=%s" % "true" if adult else "false" + params += "&include_adult=%s" % ("true" if adult else "false") if region is not None: params += "®ion=%s" % quote(region) return self._request_obj( @@ -134,7 +133,7 @@ class Search(TMDb): """ params = "query=%s&page=%s" % (quote(term), page) if adult is not None: - params += "&include_adult=%s" % "true" if adult else "false" + params += "&include_adult=%s" % ("true" if adult else "false") if release_year is not None: params += "&first_air_date_year=%s" % release_year return self._request_obj( @@ -194,7 +193,7 @@ class Search(TMDb): """ params = "query=%s&page=%s" % (quote(term), page) if adult is not None: - params += "&include_adult=%s" % "true" if adult else "false" + params += "&include_adult=%s" % ("true" if adult else "false") if region is not None: params += "®ion=%s" % quote(region) return await self._async_request_obj( @@ -215,7 +214,7 @@ class Search(TMDb): """ params = "query=%s&page=%s" % (quote(term), page) if adult is not None: - params += "&include_adult=%s" % "true" if adult else "false" + params += "&include_adult=%s" % ("true" if adult else "false") if region is not None: params += "®ion=%s" % quote(region) return await self._async_request_obj( @@ -237,7 +236,7 @@ class Search(TMDb): """ params = "query=%s&page=%s" % (quote(term), page) if adult is not None: - params += "&include_adult=%s" % "true" if adult else "false" + params += "&include_adult=%s" % ("true" if adult else "false") if region is not None: params += "®ion=%s" % quote(region) if year is not None: @@ -262,7 +261,7 @@ class Search(TMDb): """ params = "query=%s&page=%s" % (quote(term), page) if adult is not None: - params += "&include_adult=%s" % "true" if adult else "false" + params += "&include_adult=%s" % ("true" if adult else "false") if release_year is not None: params += "&first_air_date_year=%s" % release_year return await self._async_request_obj( diff --git a/tests/test_tmdb_search_parameters.py b/tests/test_tmdb_search_parameters.py new file mode 100644 index 000000000..916d01cc0 --- /dev/null +++ b/tests/test_tmdb_search_parameters.py @@ -0,0 +1,31 @@ +"""TMDb 成人内容开关须生成独立参数,不能损坏分页参数。""" + +import asyncio +from unittest.mock import AsyncMock, Mock +from urllib.parse import parse_qs + +import pytest + +from app.modules.themoviedb.tmdbv3api.objs.search import Search + + +@pytest.mark.parametrize("method", ["movies", "multi", "people", "tv_shows"]) +@pytest.mark.parametrize("asynchronous", [False, True]) +@pytest.mark.parametrize("adult", [None, False, True]) +def test_adult_option_preserves_query_and_page(monkeypatch, method, asynchronous, adult): + """双入口保持查询和页码,显式 False 与未传开关分别对应 false 与省略。""" + payload = [{"id": 42}] + request = (AsyncMock if asynchronous else Mock)(return_value=payload) + monkeypatch.setattr(Search, "_async_request_obj" if asynchronous else "_request_obj", request) + client = object.__new__(Search) + operation = getattr(client, f"async_{method}" if asynchronous else method) + arguments = {"term": "测试 & movie", "page": 2, "adult": adult} + result = asyncio.run(operation(**arguments)) if asynchronous else operation(**arguments) + assert result is payload + request.assert_called_once() + parameters = parse_qs(request.call_args.kwargs["params"]) + expected = {"query": ["测试 & movie"], "page": ["2"]} + if adult is not None: + expected["include_adult"] = ["true" if adult else "false"] + assert parameters == expected + assert request.call_args.kwargs["key"] == "results"