mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 07:27:15 +08:00
feat(indexer): API 站点支持音乐搜索(馒头/杜比/海胆/Rousi Pro/SunnyPT)
- MTorrentSpider 新增音乐分类(406 Music(音樂)),音乐搜索不再误用电影分类 - HddolbySpider 新增音乐唱片分类(408),演唱会视频(406)不计入音乐 - HaiDanSpider 新增 HQ Audio 音乐分类(408),并修复解析时从响应顶层取 category 恒为空的既有问题 - RousiSpider 支持 music 分类参数与结果标记,分类 ID 映射补充 5->music - SunnyPTSpider 分类映射与结果解析通用支持 music 媒体类型 - 新增对应站点 spider 音乐搜索单测
This commit is contained in:
@@ -28,6 +28,8 @@ class HaiDanSpider:
|
|||||||
# 电影分类
|
# 电影分类
|
||||||
_movie_category = ['401', '404', '405']
|
_movie_category = ['401', '404', '405']
|
||||||
_tv_category = ['402', '403', '404', '405']
|
_tv_category = ['402', '403', '404', '405']
|
||||||
|
# 音乐分类:408 为海胆 HQ Audio(音乐) 分区;406 MV 属于视频,不计入音乐
|
||||||
|
_music_category = ['408']
|
||||||
|
|
||||||
# 足销状态 1-普通,2-免费,3-2X,4-2X免费,5-50%,6-2X50%,7-30%
|
# 足销状态 1-普通,2-免费,3-2X,4-2X免费,5-50%,6-2X50%,7-30%
|
||||||
_dl_state = {
|
_dl_state = {
|
||||||
@@ -88,6 +90,8 @@ class HaiDanSpider:
|
|||||||
categories = []
|
categories = []
|
||||||
elif mtype == MediaType.TV:
|
elif mtype == MediaType.TV:
|
||||||
categories = self._tv_category
|
categories = self._tv_category
|
||||||
|
elif mtype == MediaType.MUSIC:
|
||||||
|
categories = self._music_category
|
||||||
else:
|
else:
|
||||||
categories = self._movie_category
|
categories = self._movie_category
|
||||||
|
|
||||||
@@ -113,8 +117,11 @@ class HaiDanSpider:
|
|||||||
torrents = []
|
torrents = []
|
||||||
data = result.get('data') or {}
|
data = result.get('data') or {}
|
||||||
for tid, item in data.items():
|
for tid, item in data.items():
|
||||||
category_value = result.get('category')
|
# 分类字段在每条种子内,接口顶层没有 category 字段
|
||||||
if category_value in self._tv_category \
|
category_value = str(item.get('category') or '')
|
||||||
|
if category_value in self._music_category:
|
||||||
|
category = MediaType.MUSIC.value
|
||||||
|
elif category_value in self._tv_category \
|
||||||
and category_value not in self._movie_category:
|
and category_value not in self._movie_category:
|
||||||
category = MediaType.TV.value
|
category = MediaType.TV.value
|
||||||
elif category_value in self._movie_category:
|
elif category_value in self._movie_category:
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ class HddolbySpider:
|
|||||||
# 分类
|
# 分类
|
||||||
_movie_category = [401, 405]
|
_movie_category = [401, 405]
|
||||||
_tv_category = [402, 403, 404, 405]
|
_tv_category = [402, 403, 404, 405]
|
||||||
|
# 音乐分类:408 为杜比音乐唱片分区;406 演唱会蓝光属于视频,不计入音乐
|
||||||
|
_music_category = [408]
|
||||||
|
|
||||||
# 标签
|
# 标签
|
||||||
_labels = {
|
_labels = {
|
||||||
@@ -88,6 +90,8 @@ class HddolbySpider:
|
|||||||
categories = self._tv_category
|
categories = self._tv_category
|
||||||
elif mtype == MediaType.MOVIE:
|
elif mtype == MediaType.MOVIE:
|
||||||
categories = self._movie_category
|
categories = self._movie_category
|
||||||
|
elif mtype == MediaType.MUSIC:
|
||||||
|
categories = self._music_category
|
||||||
else:
|
else:
|
||||||
categories = list(set(self._movie_category + self._tv_category))
|
categories = list(set(self._movie_category + self._tv_category))
|
||||||
|
|
||||||
@@ -137,7 +141,9 @@ class HddolbySpider:
|
|||||||
"""
|
"""
|
||||||
# 类别
|
# 类别
|
||||||
category_value = result.get('category')
|
category_value = result.get('category')
|
||||||
if category_value in self._tv_category:
|
if category_value in self._music_category:
|
||||||
|
category = MediaType.MUSIC.value
|
||||||
|
elif category_value in self._tv_category:
|
||||||
category = MediaType.TV.value
|
category = MediaType.TV.value
|
||||||
elif category_value in self._movie_category:
|
elif category_value in self._movie_category:
|
||||||
category = MediaType.MOVIE.value
|
category = MediaType.MOVIE.value
|
||||||
|
|||||||
@@ -34,7 +34,10 @@ class MTorrentSpider:
|
|||||||
|
|
||||||
# 电影分类
|
# 电影分类
|
||||||
_movie_category = ['401', '419', '420', '421', '439', '405', '404']
|
_movie_category = ['401', '419', '420', '421', '439', '405', '404']
|
||||||
|
# 电视剧分类
|
||||||
_tv_category = ['403', '402', '435', '438', '404', '405']
|
_tv_category = ['403', '402', '435', '438', '404', '405']
|
||||||
|
# 音乐分类:406 为馒头 Music(音樂) 分区
|
||||||
|
_music_category = ['406']
|
||||||
|
|
||||||
# API KEY
|
# API KEY
|
||||||
_apikey = None
|
_apikey = None
|
||||||
@@ -84,6 +87,8 @@ class MTorrentSpider:
|
|||||||
categories = []
|
categories = []
|
||||||
elif mtype == MediaType.TV:
|
elif mtype == MediaType.TV:
|
||||||
categories = self._tv_category
|
categories = self._tv_category
|
||||||
|
elif mtype == MediaType.MUSIC:
|
||||||
|
categories = self._music_category
|
||||||
else:
|
else:
|
||||||
categories = self._movie_category
|
categories = self._movie_category
|
||||||
# mtorrent搜索imdb需要输入完整imdb链接,参见 https://wiki.m-team.cc/zh-tw/imdbtosearch
|
# mtorrent搜索imdb需要输入完整imdb链接,参见 https://wiki.m-team.cc/zh-tw/imdbtosearch
|
||||||
@@ -107,7 +112,9 @@ class MTorrentSpider:
|
|||||||
|
|
||||||
for result in results:
|
for result in results:
|
||||||
category_value = result.get('category')
|
category_value = result.get('category')
|
||||||
if category_value in self._tv_category \
|
if category_value in self._music_category:
|
||||||
|
category = MediaType.MUSIC.value
|
||||||
|
elif category_value in self._tv_category \
|
||||||
and category_value not in self._movie_category:
|
and category_value not in self._movie_category:
|
||||||
category = MediaType.TV.value
|
category = MediaType.TV.value
|
||||||
elif category_value in self._movie_category:
|
elif category_value in self._movie_category:
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ class RousiSpider:
|
|||||||
# API 不支持多分类搜索,每次只使用一个分类
|
# API 不支持多分类搜索,每次只使用一个分类
|
||||||
_movie_category = 'movie'
|
_movie_category = 'movie'
|
||||||
_tv_category = 'tv'
|
_tv_category = 'tv'
|
||||||
|
_music_category = 'music'
|
||||||
|
|
||||||
# API KEY
|
# API KEY
|
||||||
_apikey = None
|
_apikey = None
|
||||||
@@ -67,7 +68,7 @@ class RousiSpider:
|
|||||||
构建 API 请求参数
|
构建 API 请求参数
|
||||||
|
|
||||||
:param keyword: 搜索关键词
|
:param keyword: 搜索关键词
|
||||||
:param mtype: 媒体类型 (MOVIE/TV)
|
:param mtype: 媒体类型 (MOVIE/TV/MUSIC)
|
||||||
:param cat: 用户选择的分类 ID(逗号分隔的字符串)
|
:param cat: 用户选择的分类 ID(逗号分隔的字符串)
|
||||||
:param page: 页码(从 0 开始,API 需要从 1 开始)
|
:param page: 页码(从 0 开始,API 需要从 1 开始)
|
||||||
:return: 请求参数字典
|
:return: 请求参数字典
|
||||||
@@ -93,6 +94,8 @@ class RousiSpider:
|
|||||||
params["category"] = self._movie_category
|
params["category"] = self._movie_category
|
||||||
elif mtype == MediaType.TV:
|
elif mtype == MediaType.TV:
|
||||||
params["category"] = self._tv_category
|
params["category"] = self._tv_category
|
||||||
|
elif mtype == MediaType.MUSIC:
|
||||||
|
params["category"] = self._music_category
|
||||||
|
|
||||||
return params
|
return params
|
||||||
|
|
||||||
@@ -112,6 +115,7 @@ class RousiSpider:
|
|||||||
'2': 'tv',
|
'2': 'tv',
|
||||||
'3': 'documentary',
|
'3': 'documentary',
|
||||||
'4': 'animation',
|
'4': 'animation',
|
||||||
|
'5': 'music',
|
||||||
'6': 'variety'
|
'6': 'variety'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,6 +182,8 @@ class RousiSpider:
|
|||||||
category = MediaType.MOVIE.value
|
category = MediaType.MOVIE.value
|
||||||
elif cat_val == self._tv_category:
|
elif cat_val == self._tv_category:
|
||||||
category = MediaType.TV.value
|
category = MediaType.TV.value
|
||||||
|
elif cat_val == self._music_category:
|
||||||
|
category = MediaType.MUSIC.value
|
||||||
else:
|
else:
|
||||||
category = MediaType.UNKNOWN.value
|
category = MediaType.UNKNOWN.value
|
||||||
|
|
||||||
@@ -219,7 +225,7 @@ class RousiSpider:
|
|||||||
同步搜索种子
|
同步搜索种子
|
||||||
|
|
||||||
:param keyword: 搜索关键词
|
:param keyword: 搜索关键词
|
||||||
:param mtype: 媒体类型 (MOVIE/TV)
|
:param mtype: 媒体类型 (MOVIE/TV/MUSIC)
|
||||||
:param cat: 用户选择的分类 ID(逗号分隔)
|
:param cat: 用户选择的分类 ID(逗号分隔)
|
||||||
:param page: 页码(从 0 开始)
|
:param page: 页码(从 0 开始)
|
||||||
:return: (是否发生错误, 种子列表)
|
:return: (是否发生错误, 种子列表)
|
||||||
@@ -247,7 +253,7 @@ class RousiSpider:
|
|||||||
异步搜索种子
|
异步搜索种子
|
||||||
|
|
||||||
:param keyword: 搜索关键词
|
:param keyword: 搜索关键词
|
||||||
:param mtype: 媒体类型 (MOVIE/TV)
|
:param mtype: 媒体类型 (MOVIE/TV/MUSIC)
|
||||||
:param cat: 用户选择的分类 ID(逗号分隔)
|
:param cat: 用户选择的分类 ID(逗号分隔)
|
||||||
:param page: 页码(从 0 开始)
|
:param page: 页码(从 0 开始)
|
||||||
:return: (是否发生错误, 种子列表)
|
:return: (是否发生错误, 种子列表)
|
||||||
|
|||||||
@@ -54,12 +54,12 @@ class SunnyPTSpider:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _parse_configured_categories(category_config: dict) -> dict:
|
def _parse_configured_categories(category_config: dict) -> dict:
|
||||||
"""
|
"""
|
||||||
从站点索引配置提取电影和电视剧分类 ID,作为分类接口不可用时的兜底
|
从站点索引配置提取电影、电视剧和音乐分类 ID,作为分类接口不可用时的兜底
|
||||||
|
|
||||||
:param category_config: Build 站点配置中的分类段
|
:param category_config: Build 站点配置中的分类段
|
||||||
:return: 按 API media_type 索引的分类 ID
|
:return: 按 API media_type 索引的分类 ID
|
||||||
"""
|
"""
|
||||||
category_map = {"movie": [], "tv": []}
|
category_map = {"movie": [], "tv": [], "music": []}
|
||||||
for media_type in category_map:
|
for media_type in category_map:
|
||||||
for item in category_config.get(media_type) or []:
|
for item in category_config.get(media_type) or []:
|
||||||
category_id = item.get("id") if isinstance(item, dict) else item
|
category_id = item.get("id") if isinstance(item, dict) else item
|
||||||
@@ -75,7 +75,7 @@ class SunnyPTSpider:
|
|||||||
:param items: SunnyPT 分类接口 data 数组
|
:param items: SunnyPT 分类接口 data 数组
|
||||||
:return: 按 API media_type 索引的分类 ID
|
:return: 按 API media_type 索引的分类 ID
|
||||||
"""
|
"""
|
||||||
category_map = {"movie": [], "tv": []}
|
category_map = {"movie": [], "tv": [], "music": []}
|
||||||
for item in items or []:
|
for item in items or []:
|
||||||
if not isinstance(item, dict) or item.get("id") is None:
|
if not isinstance(item, dict) or item.get("id") is None:
|
||||||
continue
|
continue
|
||||||
@@ -199,12 +199,14 @@ class SunnyPTSpider:
|
|||||||
将 MoviePilot 媒体类型转换为 SunnyPT API 枚举
|
将 MoviePilot 媒体类型转换为 SunnyPT API 枚举
|
||||||
|
|
||||||
:param media_type: MoviePilot 媒体类型
|
:param media_type: MoviePilot 媒体类型
|
||||||
:return: movie、tv 或 None
|
:return: movie、tv、music 或 None
|
||||||
"""
|
"""
|
||||||
if media_type == MediaType.MOVIE:
|
if media_type == MediaType.MOVIE:
|
||||||
return "movie"
|
return "movie"
|
||||||
if media_type == MediaType.TV:
|
if media_type == MediaType.TV:
|
||||||
return "tv"
|
return "tv"
|
||||||
|
if media_type == MediaType.MUSIC:
|
||||||
|
return "music"
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _build_params(
|
def _build_params(
|
||||||
@@ -259,6 +261,8 @@ class SunnyPTSpider:
|
|||||||
category = MediaType.MOVIE.value
|
category = MediaType.MOVIE.value
|
||||||
elif media_type == "tv":
|
elif media_type == "tv":
|
||||||
category = MediaType.TV.value
|
category = MediaType.TV.value
|
||||||
|
elif media_type == "music":
|
||||||
|
category = MediaType.MUSIC.value
|
||||||
else:
|
else:
|
||||||
category = MediaType.UNKNOWN.value
|
category = MediaType.UNKNOWN.value
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from app.modules.indexer.spider import haidan as haidan_module
|
||||||
|
from app.modules.indexer.spider.haidan import HaiDanSpider
|
||||||
|
from app.schemas import MediaType
|
||||||
|
|
||||||
|
|
||||||
|
def _build_indexer() -> dict:
|
||||||
|
"""构造海胆 API Spider 所需的最小站点配置。"""
|
||||||
|
return {
|
||||||
|
"id": "haidan",
|
||||||
|
"name": "海胆之家",
|
||||||
|
"domain": "https://www.haidan.cc/",
|
||||||
|
"cookie": "haidan-cookie",
|
||||||
|
"ua": "MoviePilot-Test",
|
||||||
|
"proxy": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def haidan_spider(monkeypatch):
|
||||||
|
"""构造不依赖真实数据库配置的 HaiDanSpider。"""
|
||||||
|
monkeypatch.setattr(haidan_module, "SystemConfigOper", lambda: None)
|
||||||
|
return HaiDanSpider(_build_indexer())
|
||||||
|
|
||||||
|
|
||||||
|
def test_music_search_uses_music_categories(haidan_spider):
|
||||||
|
"""音乐搜索应只提交海胆 HQ Audio 分区分类。"""
|
||||||
|
params = haidan_spider._HaiDanSpider__get_params("张学友", MediaType.MUSIC)
|
||||||
|
|
||||||
|
assert "cat=408" in params
|
||||||
|
assert "401" not in params
|
||||||
|
|
||||||
|
|
||||||
|
def test_movie_search_keeps_movie_categories(haidan_spider):
|
||||||
|
"""电影搜索行为不受音乐分类新增影响。"""
|
||||||
|
params = haidan_spider._HaiDanSpider__get_params("流浪地球", MediaType.MOVIE)
|
||||||
|
|
||||||
|
assert "cat=401%2C404%2C405" in params or "cat=401,404,405" in params
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_result_reads_item_category(haidan_spider):
|
||||||
|
"""分类字段取自每条种子,音乐分区应标记为音乐媒体类型。"""
|
||||||
|
result = {
|
||||||
|
"code": 0,
|
||||||
|
"data": {
|
||||||
|
"1": {"name": "张学友 - 他在那里 FLAC", "category": 408, "size": "1024"},
|
||||||
|
"2": {"name": "流浪地球", "category": 401, "size": "1024"},
|
||||||
|
"3": {"name": "剧集 S01", "category": 402, "size": "1024"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
torrents = haidan_spider._HaiDanSpider__parse_result(result)
|
||||||
|
|
||||||
|
assert [torrent["category"] for torrent in torrents] == [
|
||||||
|
MediaType.MUSIC.value,
|
||||||
|
MediaType.MOVIE.value,
|
||||||
|
MediaType.TV.value,
|
||||||
|
]
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from app.modules.indexer.spider import hddolby as hddolby_module
|
||||||
|
from app.modules.indexer.spider.hddolby import HddolbySpider
|
||||||
|
from app.schemas import MediaType
|
||||||
|
|
||||||
|
|
||||||
|
def _build_indexer() -> dict:
|
||||||
|
"""构造杜比 API Spider 所需的最小站点配置。"""
|
||||||
|
return {
|
||||||
|
"id": "hddolby",
|
||||||
|
"name": "高清杜比",
|
||||||
|
"domain": "https://www.hddolby.com/",
|
||||||
|
"apikey": "dolby-secret",
|
||||||
|
"ua": "MoviePilot-Test",
|
||||||
|
"proxy": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def hddolby_spider(monkeypatch):
|
||||||
|
"""构造不依赖真实数据库配置的 HddolbySpider。"""
|
||||||
|
monkeypatch.setattr(hddolby_module, "SystemConfigOper", lambda: None)
|
||||||
|
return HddolbySpider(_build_indexer())
|
||||||
|
|
||||||
|
|
||||||
|
def test_music_search_uses_music_categories(hddolby_spider):
|
||||||
|
"""音乐搜索应只提交杜比音乐唱片分区分类。"""
|
||||||
|
params = hddolby_spider._HddolbySpider__get_params("张学友", MediaType.MUSIC, 0)
|
||||||
|
|
||||||
|
assert params["categories"] == HddolbySpider._music_category
|
||||||
|
|
||||||
|
|
||||||
|
def test_movie_search_keeps_movie_categories(hddolby_spider):
|
||||||
|
"""电影搜索行为不受音乐分类新增影响。"""
|
||||||
|
params = hddolby_spider._HddolbySpider__get_params("流浪地球", MediaType.MOVIE, 0)
|
||||||
|
|
||||||
|
assert params["categories"] == HddolbySpider._movie_category
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_result_marks_music_torrents(hddolby_spider):
|
||||||
|
"""音乐唱片分区种子应标记为音乐媒体类型,演唱会视频仍按原逻辑处理。"""
|
||||||
|
results = hddolby_spider._HddolbySpider__parse_result([
|
||||||
|
{"id": 1, "name": "VA - Chillout 2022 FLAC", "category": 408, "size": 1024},
|
||||||
|
{"id": 2, "name": "Epica Live 1080p Blu-ray", "category": 406, "size": 1024},
|
||||||
|
{"id": 3, "name": "流浪地球 2160p", "category": 401, "size": 1024},
|
||||||
|
{"id": 4, "name": "剧集 S01", "category": 402, "size": 1024},
|
||||||
|
])
|
||||||
|
|
||||||
|
assert [torrent["category"] for torrent in results] == [
|
||||||
|
MediaType.MUSIC.value,
|
||||||
|
MediaType.UNKNOWN.value,
|
||||||
|
MediaType.MOVIE.value,
|
||||||
|
MediaType.TV.value,
|
||||||
|
]
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from app.modules.indexer.spider import mtorrent as mtorrent_module
|
||||||
|
from app.modules.indexer.spider.mtorrent import MTorrentSpider
|
||||||
|
from app.schemas import MediaType
|
||||||
|
|
||||||
|
|
||||||
|
def _build_indexer() -> dict:
|
||||||
|
"""构造 M-Team API Spider 所需的最小站点配置。"""
|
||||||
|
return {
|
||||||
|
"id": "mteam",
|
||||||
|
"name": "馒头",
|
||||||
|
"domain": "https://xp.m-team.io/",
|
||||||
|
"apikey": "mteam-secret",
|
||||||
|
"ua": "MoviePilot-Test",
|
||||||
|
"proxy": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def mteam_spider(monkeypatch):
|
||||||
|
"""构造不依赖真实数据库配置的 MTorrentSpider。"""
|
||||||
|
monkeypatch.setattr(mtorrent_module, "SystemConfigOper", lambda: None)
|
||||||
|
return MTorrentSpider(_build_indexer())
|
||||||
|
|
||||||
|
|
||||||
|
def test_music_search_uses_music_categories(mteam_spider):
|
||||||
|
"""音乐搜索应只提交馒头音乐分区分类,而不是电影分类。"""
|
||||||
|
params = mteam_spider._MTorrentSpider__get_params("周杰伦 七里香", MediaType.MUSIC)
|
||||||
|
|
||||||
|
assert params["categories"] == MTorrentSpider._music_category
|
||||||
|
|
||||||
|
|
||||||
|
def test_movie_search_keeps_movie_categories(mteam_spider):
|
||||||
|
"""电影搜索行为不受音乐分类新增影响。"""
|
||||||
|
params = mteam_spider._MTorrentSpider__get_params("流浪地球", MediaType.MOVIE)
|
||||||
|
|
||||||
|
assert params["categories"] == MTorrentSpider._movie_category
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_result_marks_music_torrents(mteam_spider):
|
||||||
|
"""音乐分区种子应标记为音乐媒体类型,供音乐搜索链路筛选。"""
|
||||||
|
results = mteam_spider._MTorrentSpider__parse_result([
|
||||||
|
{"id": "1", "name": "周杰伦 - 七里香 [FLAC]", "category": "406", "size": "1024", "status": {}},
|
||||||
|
{"id": "2", "name": "流浪地球 2160p", "category": "419", "size": "1024", "status": {}},
|
||||||
|
{"id": "3", "name": "其他资源", "category": "999", "size": "1024", "status": {}},
|
||||||
|
])
|
||||||
|
|
||||||
|
assert [torrent["category"] for torrent in results] == [
|
||||||
|
MediaType.MUSIC.value,
|
||||||
|
MediaType.MOVIE.value,
|
||||||
|
MediaType.UNKNOWN.value,
|
||||||
|
]
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from app.modules.indexer.spider import rousi as rousi_module
|
||||||
|
from app.modules.indexer.spider.rousi import RousiSpider
|
||||||
|
from app.schemas import MediaType
|
||||||
|
|
||||||
|
|
||||||
|
def _build_indexer() -> dict:
|
||||||
|
"""构造 Rousi Pro API Spider 所需的最小站点配置。"""
|
||||||
|
return {
|
||||||
|
"id": "rousipro",
|
||||||
|
"name": "Rousi Pro",
|
||||||
|
"domain": "https://rousi.pro/",
|
||||||
|
"apikey": "rousi-secret",
|
||||||
|
"ua": "MoviePilot-Test",
|
||||||
|
"proxy": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def rousi_spider(monkeypatch):
|
||||||
|
"""构造不依赖真实数据库配置的 RousiSpider。"""
|
||||||
|
monkeypatch.setattr(rousi_module, "SystemConfigOper", lambda: None)
|
||||||
|
return RousiSpider(_build_indexer())
|
||||||
|
|
||||||
|
|
||||||
|
def test_music_search_uses_music_category(rousi_spider):
|
||||||
|
"""音乐搜索应提交 Rousi Pro 的 music 分类参数。"""
|
||||||
|
params = rousi_spider._RousiSpider__get_params("张学友", MediaType.MUSIC, None, 0)
|
||||||
|
|
||||||
|
assert params["category"] == "music"
|
||||||
|
|
||||||
|
|
||||||
|
def test_user_selected_music_category_maps_to_api_name(rousi_spider):
|
||||||
|
"""用户选择音乐分类 ID 时应映射为 API 的 music 分类名。"""
|
||||||
|
params = rousi_spider._RousiSpider__get_params("张学友", None, "5", 0)
|
||||||
|
|
||||||
|
assert params["category"] == "music"
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_result_marks_music_torrents(rousi_spider):
|
||||||
|
"""music 分类种子应标记为音乐媒体类型。"""
|
||||||
|
torrents = rousi_spider._RousiSpider__parse_result([
|
||||||
|
{"id": 1, "uuid": "u1", "title": "张学友 - 他在那里 FLAC", "category": "music"},
|
||||||
|
{"id": 2, "uuid": "u2", "title": "流浪地球", "category": "movie"},
|
||||||
|
{"id": 3, "uuid": "u3", "title": "剧集 S01", "category": {"slug": "tv"}},
|
||||||
|
])
|
||||||
|
|
||||||
|
assert [torrent["category"] for torrent in torrents] == [
|
||||||
|
MediaType.MUSIC.value,
|
||||||
|
MediaType.MOVIE.value,
|
||||||
|
MediaType.TV.value,
|
||||||
|
]
|
||||||
@@ -473,3 +473,54 @@ def test_indirect_download_does_not_log_or_cache_temporary_url(monkeypatch):
|
|||||||
)
|
)
|
||||||
assert not any("sunny-secret" in message for message in log_messages)
|
assert not any("sunny-secret" in message for message in log_messages)
|
||||||
assert not any("temporary-credential" in message for message in log_messages)
|
assert not any("temporary-credential" in message for message in log_messages)
|
||||||
|
|
||||||
|
|
||||||
|
def test_sunnypt_music_search_uses_music_media_type(monkeypatch):
|
||||||
|
"""SunnyPT 音乐搜索应提交 music 媒体类型并将结果标记为音乐。"""
|
||||||
|
calls = []
|
||||||
|
|
||||||
|
def fake_get_res(request, url: str, params: dict = None, **_kwargs):
|
||||||
|
"""按请求路径回放分类和种子响应。"""
|
||||||
|
calls.append((url, params))
|
||||||
|
if url.endswith("/categories"):
|
||||||
|
return _FakeResponse({
|
||||||
|
"code": 0,
|
||||||
|
"msg": "ok",
|
||||||
|
"data": [
|
||||||
|
{"id": 408, "name": "音乐", "media_types": ["music"]},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
return _FakeResponse({
|
||||||
|
"code": 0,
|
||||||
|
"msg": "ok",
|
||||||
|
"data": {
|
||||||
|
"items": [{
|
||||||
|
"id": 321,
|
||||||
|
"title": "Album 2026 FLAC",
|
||||||
|
"media_type": "music",
|
||||||
|
"size": 1024,
|
||||||
|
"created_at": "2026-08-10T00:00:00+08:00",
|
||||||
|
"seeders": 1,
|
||||||
|
"leechers": 0,
|
||||||
|
"completed": 1,
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"app.modules.indexer.spider.sunnypt.RequestUtils.get_res",
|
||||||
|
fake_get_res,
|
||||||
|
)
|
||||||
|
|
||||||
|
error, torrents = SunnyPTSpider(_build_indexer()).search(
|
||||||
|
keyword="Album",
|
||||||
|
mtype=MediaType.MUSIC,
|
||||||
|
page=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert not error
|
||||||
|
search_url, search_params = calls[-1]
|
||||||
|
assert search_url == "https://api.sunnypt.top/api/v1/mp/torrents"
|
||||||
|
assert search_params["media_type"] == "music"
|
||||||
|
assert search_params["categories"] == "408"
|
||||||
|
assert torrents[0]["category"] == MediaType.MUSIC.value
|
||||||
|
|||||||
Reference in New Issue
Block a user