feat:媒体信息聚合
This commit is contained in:
parent
26b5ad6a44
commit
7a37078e90
@ -24,7 +24,7 @@ def calendar(page: int = 1,
|
|||||||
return [media.to_dict() for media in medias]
|
return [media.to_dict() for media in medias]
|
||||||
|
|
||||||
|
|
||||||
@router.get("/credits/{bangumiid}", summary="查询Bangumi演职员表", response_model=List[schemas.BangumiPerson])
|
@router.get("/credits/{bangumiid}", summary="查询Bangumi演职员表", response_model=List[schemas.MediaPerson])
|
||||||
def bangumi_credits(bangumiid: int,
|
def bangumi_credits(bangumiid: int,
|
||||||
page: int = 1,
|
page: int = 1,
|
||||||
count: int = 20,
|
count: int = 20,
|
||||||
@ -35,7 +35,7 @@ def bangumi_credits(bangumiid: int,
|
|||||||
persons = BangumiChain().bangumi_credits(bangumiid, page=page, count=count)
|
persons = BangumiChain().bangumi_credits(bangumiid, page=page, count=count)
|
||||||
if not persons:
|
if not persons:
|
||||||
return []
|
return []
|
||||||
return [schemas.BangumiPerson(**person) for person in persons]
|
return [schemas.MediaPerson(source='bangumi', **person) for person in persons]
|
||||||
|
|
||||||
|
|
||||||
@router.get("/recommend/{bangumiid}", summary="查询Bangumi推荐", response_model=List[schemas.MediaInfo])
|
@router.get("/recommend/{bangumiid}", summary="查询Bangumi推荐", response_model=List[schemas.MediaInfo])
|
||||||
@ -51,6 +51,40 @@ def bangumi_recommend(bangumiid: int,
|
|||||||
return [media.to_dict() for media in medias]
|
return [media.to_dict() for media in medias]
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/person/{person_id}", summary="人物详情", response_model=schemas.MediaPerson)
|
||||||
|
def bangumi_person(person_id: int,
|
||||||
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
|
"""
|
||||||
|
根据人物ID查询人物详情
|
||||||
|
"""
|
||||||
|
personinfo = BangumiChain().person_detail(person_id=person_id)
|
||||||
|
if not personinfo:
|
||||||
|
return schemas.MediaPerson(source='bangumi')
|
||||||
|
else:
|
||||||
|
return schemas.MediaPerson(source='bangumi', **{
|
||||||
|
"id": personinfo.get("id"),
|
||||||
|
"name": personinfo.get("name"),
|
||||||
|
"images": personinfo.get("images"),
|
||||||
|
"biography": personinfo.get("summary"),
|
||||||
|
"birthday": personinfo.get("birth_day"),
|
||||||
|
"gender": personinfo.get("gender")
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/person/credits/{person_id}", summary="人物参演作品", response_model=List[schemas.MediaInfo])
|
||||||
|
def bangumi_person_credits(person_id: int,
|
||||||
|
page: int = 1,
|
||||||
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
|
"""
|
||||||
|
根据人物ID查询人物参演作品
|
||||||
|
"""
|
||||||
|
infos = BangumiChain().person_credits(person_id=person_id, page=page)
|
||||||
|
if not infos:
|
||||||
|
return []
|
||||||
|
else:
|
||||||
|
return [MediaInfo(bangumi_info=info).to_dict() for info in infos]
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{bangumiid}", summary="查询Bangumi详情", response_model=schemas.MediaInfo)
|
@router.get("/{bangumiid}", summary="查询Bangumi详情", response_model=schemas.MediaInfo)
|
||||||
def bangumi_info(bangumiid: int,
|
def bangumi_info(bangumiid: int,
|
||||||
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
|
@ -28,6 +28,43 @@ def douban_img(imgurl: str) -> Any:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/person/{person_id}", summary="人物详情", response_model=schemas.MediaPerson)
|
||||||
|
def douban_person(person_id: int,
|
||||||
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
|
"""
|
||||||
|
根据人物ID查询人物详情
|
||||||
|
"""
|
||||||
|
personinfo = DoubanChain().person_detail(person_id=person_id)
|
||||||
|
if not personinfo:
|
||||||
|
return schemas.MediaPerson(source='douban')
|
||||||
|
else:
|
||||||
|
also_known_as = []
|
||||||
|
infos = personinfo.get("extra", {}).get("info")
|
||||||
|
if infos:
|
||||||
|
also_known_as = [":".join(info) for info in infos]
|
||||||
|
return schemas.MediaPerson(source='douban', **{
|
||||||
|
"id": personinfo.get("id"),
|
||||||
|
"name": personinfo.get("title"),
|
||||||
|
"avatar": personinfo.get("cover_img", {}).get("url"),
|
||||||
|
"biography": personinfo.get("extra", {}).get("short_info"),
|
||||||
|
"also_known_as": also_known_as,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/person/credits/{person_id}", summary="人物参演作品", response_model=List[schemas.MediaInfo])
|
||||||
|
def douban_person_credits(person_id: int,
|
||||||
|
page: int = 1,
|
||||||
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
|
"""
|
||||||
|
根据人物ID查询人物参演作品
|
||||||
|
"""
|
||||||
|
works = DoubanChain().person_credits(person_id=person_id, page=page)
|
||||||
|
if not works:
|
||||||
|
return []
|
||||||
|
else:
|
||||||
|
return [MediaInfo(douban_info=work.get("subject")).to_dict() for work in works]
|
||||||
|
|
||||||
|
|
||||||
@router.get("/showing", summary="豆瓣正在热映", response_model=List[schemas.MediaInfo])
|
@router.get("/showing", summary="豆瓣正在热映", response_model=List[schemas.MediaInfo])
|
||||||
def movie_showing(page: int = 1,
|
def movie_showing(page: int = 1,
|
||||||
count: int = 30,
|
count: int = 30,
|
||||||
@ -149,13 +186,13 @@ def tv_hot(page: int = 1,
|
|||||||
return [MediaInfo(douban_info=tv).to_dict() for tv in tvs]
|
return [MediaInfo(douban_info=tv).to_dict() for tv in tvs]
|
||||||
|
|
||||||
|
|
||||||
@router.get("/credits/{doubanid}/{type_name}", summary="豆瓣演员阵容", response_model=List[schemas.DoubanPerson])
|
@router.get("/credits/{doubanid}/{type_name}", summary="豆瓣演员阵容", response_model=List[schemas.MediaPerson])
|
||||||
def douban_credits(doubanid: str,
|
def douban_credits(doubanid: str,
|
||||||
type_name: str,
|
type_name: str,
|
||||||
page: int = 1,
|
page: int = 1,
|
||||||
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
根据TMDBID查询演员阵容,type_name: 电影/电视剧
|
根据豆瓣ID查询演员阵容,type_name: 电影/电视剧
|
||||||
"""
|
"""
|
||||||
mediatype = MediaType(type_name)
|
mediatype = MediaType(type_name)
|
||||||
if mediatype == MediaType.MOVIE:
|
if mediatype == MediaType.MOVIE:
|
||||||
@ -167,7 +204,10 @@ def douban_credits(doubanid: str,
|
|||||||
if not doubaninfos:
|
if not doubaninfos:
|
||||||
return []
|
return []
|
||||||
else:
|
else:
|
||||||
return [schemas.DoubanPerson(**doubaninfo) for doubaninfo in doubaninfos]
|
# 更新豆瓣演员信息中的ID,从URI中提取'douban://douban.com/celebrity/1316132?subject_id=27503705' subject_id
|
||||||
|
for doubaninfo in doubaninfos:
|
||||||
|
doubaninfo['id'] = doubaninfo.get('uri', '').split('?subject_id=')[-1]
|
||||||
|
return [schemas.MediaPerson(source='douban', **doubaninfo) for doubaninfo in doubaninfos]
|
||||||
|
|
||||||
|
|
||||||
@router.get("/recommend/{doubanid}/{type_name}", summary="豆瓣推荐电影/电视剧", response_model=List[schemas.MediaInfo])
|
@router.get("/recommend/{doubanid}/{type_name}", summary="豆瓣推荐电影/电视剧", response_model=List[schemas.MediaInfo])
|
||||||
|
@ -61,25 +61,6 @@ def exists(title: str = None,
|
|||||||
ret_info = {
|
ret_info = {
|
||||||
"id": exist.item_id
|
"id": exist.item_id
|
||||||
}
|
}
|
||||||
"""
|
|
||||||
else:
|
|
||||||
# 服务器是否存在
|
|
||||||
mediainfo = MediaInfo()
|
|
||||||
mediainfo.from_dict({
|
|
||||||
"title": meta.name,
|
|
||||||
"year": year or meta.year,
|
|
||||||
"type": mtype or meta.type,
|
|
||||||
"tmdb_id": tmdbid,
|
|
||||||
"season": season
|
|
||||||
})
|
|
||||||
exist: schemas.ExistMediaInfo = MediaServerChain().media_exists(
|
|
||||||
mediainfo=mediainfo
|
|
||||||
)
|
|
||||||
if exist:
|
|
||||||
ret_info = {
|
|
||||||
"id": exist.itemid
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
return schemas.Response(success=True if exist else False, data={
|
return schemas.Response(success=True if exist else False, data={
|
||||||
"item": ret_info
|
"item": ret_info
|
||||||
})
|
})
|
||||||
|
@ -63,7 +63,7 @@ def tmdb_recommend(tmdbid: int,
|
|||||||
return [MediaInfo(tmdb_info=tmdbinfo).to_dict() for tmdbinfo in tmdbinfos]
|
return [MediaInfo(tmdb_info=tmdbinfo).to_dict() for tmdbinfo in tmdbinfos]
|
||||||
|
|
||||||
|
|
||||||
@router.get("/credits/{tmdbid}/{type_name}", summary="演员阵容", response_model=List[schemas.TmdbPerson])
|
@router.get("/credits/{tmdbid}/{type_name}", summary="演员阵容", response_model=List[schemas.MediaPerson])
|
||||||
def tmdb_credits(tmdbid: int,
|
def tmdb_credits(tmdbid: int,
|
||||||
type_name: str,
|
type_name: str,
|
||||||
page: int = 1,
|
page: int = 1,
|
||||||
@ -81,10 +81,10 @@ def tmdb_credits(tmdbid: int,
|
|||||||
if not tmdbinfos:
|
if not tmdbinfos:
|
||||||
return []
|
return []
|
||||||
else:
|
else:
|
||||||
return [schemas.TmdbPerson(**tmdbinfo) for tmdbinfo in tmdbinfos]
|
return [schemas.MediaPerson(source='themoviedb', **tmdbinfo) for tmdbinfo in tmdbinfos]
|
||||||
|
|
||||||
|
|
||||||
@router.get("/person/{person_id}", summary="人物详情", response_model=schemas.TmdbPerson)
|
@router.get("/person/{person_id}", summary="人物详情", response_model=schemas.MediaPerson)
|
||||||
def tmdb_person(person_id: int,
|
def tmdb_person(person_id: int,
|
||||||
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
@ -92,9 +92,9 @@ def tmdb_person(person_id: int,
|
|||||||
"""
|
"""
|
||||||
tmdbinfo = TmdbChain().person_detail(person_id=person_id)
|
tmdbinfo = TmdbChain().person_detail(person_id=person_id)
|
||||||
if not tmdbinfo:
|
if not tmdbinfo:
|
||||||
return schemas.TmdbPerson()
|
return schemas.MediaPerson(source='themoviedb')
|
||||||
else:
|
else:
|
||||||
return schemas.TmdbPerson(**tmdbinfo)
|
return schemas.MediaPerson(source='themoviedb', **tmdbinfo)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/person/credits/{person_id}", summary="人物参演作品", response_model=List[schemas.MediaInfo])
|
@router.get("/person/credits/{person_id}", summary="人物参演作品", response_model=List[schemas.MediaInfo])
|
||||||
|
@ -19,7 +19,7 @@ from app.db.message_oper import MessageOper
|
|||||||
from app.helper.message import MessageHelper
|
from app.helper.message import MessageHelper
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.schemas import TransferInfo, TransferTorrent, ExistMediaInfo, DownloadingTorrent, CommingMessage, Notification, \
|
from app.schemas import TransferInfo, TransferTorrent, ExistMediaInfo, DownloadingTorrent, CommingMessage, Notification, \
|
||||||
WebhookEventInfo, TmdbEpisode, TmdbPerson
|
WebhookEventInfo, TmdbEpisode, MediaPerson
|
||||||
from app.schemas.types import TorrentStatus, MediaType, MediaImageType, EventType
|
from app.schemas.types import TorrentStatus, MediaType, MediaImageType, EventType
|
||||||
from app.utils.object import ObjectUtils
|
from app.utils.object import ObjectUtils
|
||||||
|
|
||||||
@ -260,7 +260,7 @@ class ChainBase(metaclass=ABCMeta):
|
|||||||
"""
|
"""
|
||||||
return self.run_module("search_medias", meta=meta)
|
return self.run_module("search_medias", meta=meta)
|
||||||
|
|
||||||
def search_persons(self, name: str) -> Optional[List[TmdbPerson]]:
|
def search_persons(self, name: str) -> Optional[List[MediaPerson]]:
|
||||||
"""
|
"""
|
||||||
搜索人物信息
|
搜索人物信息
|
||||||
:param name: 人物名称
|
:param name: 人物名称
|
||||||
|
@ -40,3 +40,18 @@ class BangumiChain(ChainBase, metaclass=Singleton):
|
|||||||
:param bangumiid: BangumiID
|
:param bangumiid: BangumiID
|
||||||
"""
|
"""
|
||||||
return self.run_module("bangumi_recommend", bangumiid=bangumiid)
|
return self.run_module("bangumi_recommend", bangumiid=bangumiid)
|
||||||
|
|
||||||
|
def person_detail(self, person_id: int) -> dict:
|
||||||
|
"""
|
||||||
|
根据人物ID查询Bangumi人物详情
|
||||||
|
:param person_id: 人物ID
|
||||||
|
"""
|
||||||
|
return self.run_module("bangumi_person_detail", person_id=person_id)
|
||||||
|
|
||||||
|
def person_credits(self, person_id: int, page: int = 1) -> List[dict]:
|
||||||
|
"""
|
||||||
|
根据人物ID查询人物参演作品
|
||||||
|
:param person_id: 人物ID
|
||||||
|
:param page: 页码
|
||||||
|
"""
|
||||||
|
return self.run_module("bangumi_person_credits", person_id=person_id, page=page)
|
||||||
|
@ -11,6 +11,21 @@ class DoubanChain(ChainBase, metaclass=Singleton):
|
|||||||
豆瓣处理链,单例运行
|
豆瓣处理链,单例运行
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def person_detail(self, person_id: int) -> dict:
|
||||||
|
"""
|
||||||
|
根据人物ID查询豆瓣人物详情
|
||||||
|
:param person_id: 人物ID
|
||||||
|
"""
|
||||||
|
return self.run_module("douban_person_detail", person_id=person_id)
|
||||||
|
|
||||||
|
def person_credits(self, person_id: int, page: int = 1) -> List[dict]:
|
||||||
|
"""
|
||||||
|
根据人物ID查询人物参演作品
|
||||||
|
:param person_id: 人物ID
|
||||||
|
:param page: 页码
|
||||||
|
"""
|
||||||
|
return self.run_module("douban_person_credits", person_id=person_id, page=page)
|
||||||
|
|
||||||
def movie_top250(self, page: int = 1, count: int = 30) -> Optional[List[dict]]:
|
def movie_top250(self, page: int = 1, count: int = 30) -> Optional[List[dict]]:
|
||||||
"""
|
"""
|
||||||
获取豆瓣电影TOP250
|
获取豆瓣电影TOP250
|
||||||
|
@ -10,7 +10,7 @@ from app.core.event import eventmanager, Event
|
|||||||
from app.core.meta import MetaBase
|
from app.core.meta import MetaBase
|
||||||
from app.core.metainfo import MetaInfo, MetaInfoPath
|
from app.core.metainfo import MetaInfo, MetaInfoPath
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.schemas import TmdbPerson
|
from app.schemas import MediaPerson
|
||||||
from app.schemas.types import EventType, MediaType
|
from app.schemas.types import EventType, MediaType
|
||||||
from app.utils.singleton import Singleton
|
from app.utils.singleton import Singleton
|
||||||
from app.utils.string import StringUtils
|
from app.utils.string import StringUtils
|
||||||
@ -158,7 +158,7 @@ class MediaChain(ChainBase, metaclass=Singleton):
|
|||||||
return Context(meta_info=file_meta, media_info=mediainfo)
|
return Context(meta_info=file_meta, media_info=mediainfo)
|
||||||
|
|
||||||
def search(self, title: str,
|
def search(self, title: str,
|
||||||
stype: str = "media") -> Tuple[Optional[MetaBase], List[Union[MediaInfo, TmdbPerson]]]:
|
stype: str = "media") -> Tuple[Optional[MetaBase], List[Union[MediaInfo, MediaPerson]]]:
|
||||||
"""
|
"""
|
||||||
搜索媒体/人物信息
|
搜索媒体/人物信息
|
||||||
:param title: 搜索内容
|
:param title: 搜索内容
|
||||||
|
@ -133,6 +133,8 @@ class TorrentInfo:
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class MediaInfo:
|
class MediaInfo:
|
||||||
|
# 来源:themoviedb、douban、bangumi
|
||||||
|
source: str = None
|
||||||
# 类型 电影、电视剧
|
# 类型 电影、电视剧
|
||||||
type: MediaType = None
|
type: MediaType = None
|
||||||
# 媒体标题
|
# 媒体标题
|
||||||
@ -355,6 +357,8 @@ class MediaInfo:
|
|||||||
|
|
||||||
if not info:
|
if not info:
|
||||||
return
|
return
|
||||||
|
# 来源
|
||||||
|
self.source = "themoviedb"
|
||||||
# 本体
|
# 本体
|
||||||
self.tmdb_info = info
|
self.tmdb_info = info
|
||||||
# 类型
|
# 类型
|
||||||
@ -440,6 +444,8 @@ class MediaInfo:
|
|||||||
"""
|
"""
|
||||||
if not info:
|
if not info:
|
||||||
return
|
return
|
||||||
|
# 来源
|
||||||
|
self.source = "douban"
|
||||||
# 本体
|
# 本体
|
||||||
self.douban_info = info
|
self.douban_info = info
|
||||||
# 豆瓣ID
|
# 豆瓣ID
|
||||||
@ -448,6 +454,8 @@ class MediaInfo:
|
|||||||
if not self.type:
|
if not self.type:
|
||||||
if isinstance(info.get('media_type'), MediaType):
|
if isinstance(info.get('media_type'), MediaType):
|
||||||
self.type = info.get('media_type')
|
self.type = info.get('media_type')
|
||||||
|
elif info.get("subtype"):
|
||||||
|
self.type = MediaType.MOVIE if info.get("subtype") == "movie" else MediaType.TV
|
||||||
elif info.get("type"):
|
elif info.get("type"):
|
||||||
self.type = MediaType.MOVIE if info.get("type") == "movie" else MediaType.TV
|
self.type = MediaType.MOVIE if info.get("type") == "movie" else MediaType.TV
|
||||||
elif info.get("type_name"):
|
elif info.get("type_name"):
|
||||||
@ -464,6 +472,8 @@ class MediaInfo:
|
|||||||
# 年份
|
# 年份
|
||||||
if not self.year:
|
if not self.year:
|
||||||
self.year = info.get("year")[:4] if info.get("year") else None
|
self.year = info.get("year")[:4] if info.get("year") else None
|
||||||
|
if not self.year and info.get("extra"):
|
||||||
|
self.year = info.get("extra").get("year")
|
||||||
# 识别标题中的季
|
# 识别标题中的季
|
||||||
meta = MetaInfo(info.get("title"))
|
meta = MetaInfo(info.get("title"))
|
||||||
# 季
|
# 季
|
||||||
@ -498,10 +508,18 @@ class MediaInfo:
|
|||||||
if not self.poster_path and info.get("cover_url"):
|
if not self.poster_path and info.get("cover_url"):
|
||||||
self.poster_path = info.get("cover_url")
|
self.poster_path = info.get("cover_url")
|
||||||
if not self.poster_path and info.get("cover"):
|
if not self.poster_path and info.get("cover"):
|
||||||
|
if info.get("cover").get("url"):
|
||||||
self.poster_path = info.get("cover").get("url")
|
self.poster_path = info.get("cover").get("url")
|
||||||
|
else:
|
||||||
|
self.poster_path = info.get("cover").get("large", {}).get("url")
|
||||||
# 简介
|
# 简介
|
||||||
if not self.overview:
|
if not self.overview:
|
||||||
self.overview = info.get("intro") or info.get("card_subtitle") or ""
|
self.overview = info.get("intro") or info.get("card_subtitle") or ""
|
||||||
|
if not self.overview:
|
||||||
|
if info.get("extra", {}).get("info"):
|
||||||
|
extra_info = info.get("extra").get("info")
|
||||||
|
if extra_info:
|
||||||
|
self.overview = ",".join([":".join(item) for item in extra_info])
|
||||||
# 从简介中提取年份
|
# 从简介中提取年份
|
||||||
if self.overview and not self.year:
|
if self.overview and not self.year:
|
||||||
match = re.search(r'\d{4}', self.overview)
|
match = re.search(r'\d{4}', self.overview)
|
||||||
@ -553,6 +571,8 @@ class MediaInfo:
|
|||||||
"""
|
"""
|
||||||
if not info:
|
if not info:
|
||||||
return
|
return
|
||||||
|
# 来源
|
||||||
|
self.source = "bangumi"
|
||||||
# 本体
|
# 本体
|
||||||
self.bangumi_info = info
|
self.bangumi_info = info
|
||||||
# 豆瓣ID
|
# 豆瓣ID
|
||||||
@ -589,6 +609,8 @@ class MediaInfo:
|
|||||||
if not self.poster_path:
|
if not self.poster_path:
|
||||||
if info.get("images"):
|
if info.get("images"):
|
||||||
self.poster_path = info.get("images", {}).get("large")
|
self.poster_path = info.get("images", {}).get("large")
|
||||||
|
if not self.poster_path and info.get("image"):
|
||||||
|
self.poster_path = info.get("image")
|
||||||
# 简介
|
# 简介
|
||||||
if not self.overview:
|
if not self.overview:
|
||||||
self.overview = info.get("summary")
|
self.overview = info.get("summary")
|
||||||
|
@ -79,7 +79,7 @@ class BangumiModule(_ModuleBase):
|
|||||||
:param page: 页码
|
:param page: 页码
|
||||||
:param count: 数量
|
:param count: 数量
|
||||||
"""
|
"""
|
||||||
persons = self.bangumiapi.persons(bangumiid) or []
|
persons = self.bangumiapi.credits(bangumiid)
|
||||||
if persons:
|
if persons:
|
||||||
return persons[(page - 1) * count: page * count]
|
return persons[(page - 1) * count: page * count]
|
||||||
else:
|
else:
|
||||||
@ -90,4 +90,19 @@ class BangumiModule(_ModuleBase):
|
|||||||
根据BangumiID查询推荐电影
|
根据BangumiID查询推荐电影
|
||||||
:param bangumiid: BangumiID
|
:param bangumiid: BangumiID
|
||||||
"""
|
"""
|
||||||
return self.bangumiapi.subjects(bangumiid) or []
|
return self.bangumiapi.subjects(bangumiid)
|
||||||
|
|
||||||
|
def bangumi_person_detail(self, person_id: int) -> dict:
|
||||||
|
"""
|
||||||
|
获取人物详细信息
|
||||||
|
:param person_id: 豆瓣人物ID
|
||||||
|
"""
|
||||||
|
return self.bangumiapi.person_detail(person_id)
|
||||||
|
|
||||||
|
def bangumi_person_credits(self, person_id: int, page: int = 1) -> List[dict]:
|
||||||
|
"""
|
||||||
|
根据TMDBID查询人物参演作品
|
||||||
|
:param person_id: 人物ID
|
||||||
|
:param page: 页码
|
||||||
|
"""
|
||||||
|
return self.bangumiapi.person_credits(person_id=person_id, page=page)
|
||||||
|
@ -14,9 +14,11 @@ class BangumiApi(object):
|
|||||||
_urls = {
|
_urls = {
|
||||||
"calendar": "calendar",
|
"calendar": "calendar",
|
||||||
"detail": "v0/subjects/%s",
|
"detail": "v0/subjects/%s",
|
||||||
"persons": "v0/subjects/%s/persons",
|
"credits": "v0/subjects/%s/persons",
|
||||||
"subjects": "v0/subjects/%s/subjects",
|
"subjects": "v0/subjects/%s/subjects",
|
||||||
"characters": "v0/subjects/%s/characters"
|
"characters": "v0/subjects/%s/characters",
|
||||||
|
"person_detail": "v0/persons/%s",
|
||||||
|
"person_credits": "v0/persons/%s/subjects",
|
||||||
}
|
}
|
||||||
_base_url = "https://api.bgm.tv/"
|
_base_url = "https://api.bgm.tv/"
|
||||||
_req = RequestUtils(session=requests.Session())
|
_req = RequestUtils(session=requests.Session())
|
||||||
@ -142,7 +144,7 @@ class BangumiApi(object):
|
|||||||
"""
|
"""
|
||||||
return self.__invoke(self._urls["detail"] % bid, _ts=datetime.strftime(datetime.now(), '%Y%m%d'))
|
return self.__invoke(self._urls["detail"] % bid, _ts=datetime.strftime(datetime.now(), '%Y%m%d'))
|
||||||
|
|
||||||
def persons(self, bid: int):
|
def credits(self, bid: int):
|
||||||
"""
|
"""
|
||||||
获取番剧人物
|
获取番剧人物
|
||||||
"""
|
"""
|
||||||
@ -163,3 +165,20 @@ class BangumiApi(object):
|
|||||||
获取关联条目信息
|
获取关联条目信息
|
||||||
"""
|
"""
|
||||||
return self.__invoke(self._urls["subjects"] % bid, _ts=datetime.strftime(datetime.now(), '%Y%m%d'))
|
return self.__invoke(self._urls["subjects"] % bid, _ts=datetime.strftime(datetime.now(), '%Y%m%d'))
|
||||||
|
|
||||||
|
def person_detail(self, person_id: int):
|
||||||
|
"""
|
||||||
|
获取人物详细信息
|
||||||
|
"""
|
||||||
|
return self.__invoke(self._urls["person_detail"] % person_id, _ts=datetime.strftime(datetime.now(), '%Y%m%d'))
|
||||||
|
|
||||||
|
def person_credits(self, person_id: int, page: int = 1):
|
||||||
|
"""
|
||||||
|
获取人物参演作品
|
||||||
|
"""
|
||||||
|
ret_list = []
|
||||||
|
result = self.__invoke(self._urls["person_credits"] % person_id, _ts=datetime.strftime(datetime.now(), '%Y%m%d'))
|
||||||
|
if result:
|
||||||
|
for item in result:
|
||||||
|
ret_list.append(item)
|
||||||
|
return ret_list[(page - 1) * 20: page * 20]
|
||||||
|
@ -13,6 +13,7 @@ from app.modules import _ModuleBase
|
|||||||
from app.modules.douban.apiv2 import DoubanApi
|
from app.modules.douban.apiv2 import DoubanApi
|
||||||
from app.modules.douban.douban_cache import DoubanCache
|
from app.modules.douban.douban_cache import DoubanCache
|
||||||
from app.modules.douban.scraper import DoubanScraper
|
from app.modules.douban.scraper import DoubanScraper
|
||||||
|
from app.schemas import MediaPerson
|
||||||
from app.schemas.types import MediaType
|
from app.schemas.types import MediaType
|
||||||
from app.utils.common import retry
|
from app.utils.common import retry
|
||||||
from app.utils.http import RequestUtils
|
from app.utils.http import RequestUtils
|
||||||
@ -536,10 +537,6 @@ class DoubanModule(_ModuleBase):
|
|||||||
:param meta: 识别的元数据
|
:param meta: 识别的元数据
|
||||||
:reutrn: 媒体信息
|
:reutrn: 媒体信息
|
||||||
"""
|
"""
|
||||||
# 未启用豆瓣搜索时返回None
|
|
||||||
if settings.RECOGNIZE_SOURCE != "douban":
|
|
||||||
return None
|
|
||||||
|
|
||||||
if not meta.name:
|
if not meta.name:
|
||||||
return []
|
return []
|
||||||
result = self.doubanapi.search(meta.name)
|
result = self.doubanapi.search(meta.name)
|
||||||
@ -563,6 +560,23 @@ class DoubanModule(_ModuleBase):
|
|||||||
media.season = meta.begin_season
|
media.season = meta.begin_season
|
||||||
return ret_medias
|
return ret_medias
|
||||||
|
|
||||||
|
def search_persons(self, name: str) -> Optional[List[MediaPerson]]:
|
||||||
|
"""
|
||||||
|
搜索人物信息
|
||||||
|
"""
|
||||||
|
if not name:
|
||||||
|
return []
|
||||||
|
result = self.doubanapi.person_search(keyword=name)
|
||||||
|
if result and result.get('items'):
|
||||||
|
return [MediaPerson(source='douban', **{
|
||||||
|
'id': item.get('target_id'),
|
||||||
|
'name': item.get('target', {}).get('title'),
|
||||||
|
'url': item.get('target', {}).get('url'),
|
||||||
|
'images': item.get('target', {}).get('cover', {}),
|
||||||
|
'avatar': item.get('target', {}).get('cover_img', {}).get('url'),
|
||||||
|
}) for item in result.get('items')]
|
||||||
|
return []
|
||||||
|
|
||||||
@retry(Exception, 5, 3, 3, logger=logger)
|
@retry(Exception, 5, 3, 3, logger=logger)
|
||||||
def match_doubaninfo(self, name: str, imdbid: str = None,
|
def match_doubaninfo(self, name: str, imdbid: str = None,
|
||||||
mtype: MediaType = None, year: str = None, season: int = None) -> dict:
|
mtype: MediaType = None, year: str = None, season: int = None) -> dict:
|
||||||
@ -804,11 +818,39 @@ class DoubanModule(_ModuleBase):
|
|||||||
根据豆瓣ID查询推荐电影
|
根据豆瓣ID查询推荐电影
|
||||||
:param doubanid: 豆瓣ID
|
:param doubanid: 豆瓣ID
|
||||||
"""
|
"""
|
||||||
return self.doubanapi.movie_recommendations(subject_id=doubanid) or []
|
return self.doubanapi.movie_recommendations(subject_id=doubanid)
|
||||||
|
|
||||||
def douban_tv_recommend(self, doubanid: str) -> List[dict]:
|
def douban_tv_recommend(self, doubanid: str) -> List[dict]:
|
||||||
"""
|
"""
|
||||||
根据豆瓣ID查询推荐电视剧
|
根据豆瓣ID查询推荐电视剧
|
||||||
:param doubanid: 豆瓣ID
|
:param doubanid: 豆瓣ID
|
||||||
"""
|
"""
|
||||||
return self.doubanapi.tv_recommendations(subject_id=doubanid) or []
|
return self.doubanapi.tv_recommendations(subject_id=doubanid)
|
||||||
|
|
||||||
|
def douban_person_detail(self, person_id: int) -> dict:
|
||||||
|
"""
|
||||||
|
获取人物详细信息
|
||||||
|
:param person_id: 豆瓣人物ID
|
||||||
|
"""
|
||||||
|
return self.doubanapi.person_detail(person_id)
|
||||||
|
|
||||||
|
def douban_person_credits(self, person_id: int, page: int = 1) -> List[dict]:
|
||||||
|
"""
|
||||||
|
根据TMDBID查询人物参演作品
|
||||||
|
:param person_id: 人物ID
|
||||||
|
:param page: 页码
|
||||||
|
"""
|
||||||
|
# 获取人物参演作品集
|
||||||
|
personinfo = self.doubanapi.person_detail(person_id)
|
||||||
|
if not personinfo:
|
||||||
|
return []
|
||||||
|
collection_id = None
|
||||||
|
for module in personinfo.get("modules"):
|
||||||
|
if module.get("type") == "work_collections":
|
||||||
|
collection_id = module.get("payload", {}).get("id")
|
||||||
|
# 查询作品集内容
|
||||||
|
if collection_id:
|
||||||
|
collections = self.doubanapi.person_work(subject_id=collection_id, start=(page - 1) * 20, count=20)
|
||||||
|
if collections:
|
||||||
|
return collections.get("works")
|
||||||
|
return []
|
||||||
|
@ -488,15 +488,16 @@ class DoubanApi(metaclass=Singleton):
|
|||||||
return self.__invoke(self._urls["tv_photos"] % subject_id,
|
return self.__invoke(self._urls["tv_photos"] % subject_id,
|
||||||
start=start, count=count, _ts=ts)
|
start=start, count=count, _ts=ts)
|
||||||
|
|
||||||
def person_detail(self, subject_id):
|
def person_detail(self, subject_id: int):
|
||||||
"""
|
"""
|
||||||
用户详情
|
用户详情
|
||||||
:param subject_id: 人物 id
|
:param subject_id: 人物 id
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
return self.__invoke(self._urls["person_detail"] + subject_id)
|
return self.__invoke(self._urls["person_detail"] + str(subject_id))
|
||||||
|
|
||||||
def person_work(self, subject_id, start=0, count=20, sort_by="time", collection_title="影视",
|
def person_work(self, subject_id: int, start: int = 0, count: int = 20, sort_by: str = "time",
|
||||||
|
collection_title: str = "影视",
|
||||||
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
|
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
|
||||||
"""
|
"""
|
||||||
用户作品集
|
用户作品集
|
||||||
|
@ -13,7 +13,7 @@ from app.modules.themoviedb.category import CategoryHelper
|
|||||||
from app.modules.themoviedb.scraper import TmdbScraper
|
from app.modules.themoviedb.scraper import TmdbScraper
|
||||||
from app.modules.themoviedb.tmdb_cache import TmdbCache
|
from app.modules.themoviedb.tmdb_cache import TmdbCache
|
||||||
from app.modules.themoviedb.tmdbapi import TmdbApi
|
from app.modules.themoviedb.tmdbapi import TmdbApi
|
||||||
from app.schemas import TmdbPerson
|
from app.schemas import MediaPerson
|
||||||
from app.schemas.types import MediaType, MediaImageType
|
from app.schemas.types import MediaType, MediaImageType
|
||||||
from app.utils.http import RequestUtils
|
from app.utils.http import RequestUtils
|
||||||
from app.utils.system import SystemUtils
|
from app.utils.system import SystemUtils
|
||||||
@ -227,10 +227,6 @@ class TheMovieDbModule(_ModuleBase):
|
|||||||
:param meta: 识别的元数据
|
:param meta: 识别的元数据
|
||||||
:reutrn: 媒体信息列表
|
:reutrn: 媒体信息列表
|
||||||
"""
|
"""
|
||||||
# 未启用时返回None
|
|
||||||
if settings.RECOGNIZE_SOURCE != "themoviedb":
|
|
||||||
return None
|
|
||||||
|
|
||||||
if not meta.name:
|
if not meta.name:
|
||||||
return []
|
return []
|
||||||
if meta.type == MediaType.UNKNOWN and not meta.year:
|
if meta.type == MediaType.UNKNOWN and not meta.year:
|
||||||
@ -262,17 +258,15 @@ class TheMovieDbModule(_ModuleBase):
|
|||||||
return medias
|
return medias
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def search_persons(self, name: str) -> Optional[List[TmdbPerson]]:
|
def search_persons(self, name: str) -> Optional[List[MediaPerson]]:
|
||||||
"""
|
"""
|
||||||
搜索人物信息
|
搜索人物信息
|
||||||
"""
|
"""
|
||||||
if settings.RECOGNIZE_SOURCE != "themoviedb":
|
|
||||||
return None
|
|
||||||
if not name:
|
if not name:
|
||||||
return []
|
return []
|
||||||
results = self.tmdb.search_persons(name)
|
results = self.tmdb.search_persons(name)
|
||||||
if results:
|
if results:
|
||||||
return [TmdbPerson(**person) for person in results]
|
return [MediaPerson(source='themoviedb', **person) for person in results]
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def scrape_metadata(self, path: Path, mediainfo: MediaInfo, transfer_type: str,
|
def scrape_metadata(self, path: Path, mediainfo: MediaInfo, transfer_type: str,
|
||||||
|
@ -14,5 +14,3 @@ from .message import *
|
|||||||
from .tmdb import *
|
from .tmdb import *
|
||||||
from .transfer import *
|
from .transfer import *
|
||||||
from .file import *
|
from .file import *
|
||||||
from .bangumi import *
|
|
||||||
from .douban import *
|
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
from typing import Optional
|
|
||||||
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
|
|
||||||
class BangumiPerson(BaseModel):
|
|
||||||
id: Optional[int] = None
|
|
||||||
name: Optional[str] = None
|
|
||||||
type: Optional[int] = 1
|
|
||||||
career: Optional[list] = []
|
|
||||||
images: Optional[dict] = {}
|
|
||||||
relation: Optional[str] = None
|
|
@ -1,4 +1,4 @@
|
|||||||
from typing import Optional, Dict, List
|
from typing import Optional, Dict, List, Union
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
@ -63,6 +63,8 @@ class MediaInfo(BaseModel):
|
|||||||
"""
|
"""
|
||||||
识别媒体信息
|
识别媒体信息
|
||||||
"""
|
"""
|
||||||
|
# 来源:themoviedb、douban、bangumi
|
||||||
|
source: Optional[str] = None
|
||||||
# 类型 电影、电视剧
|
# 类型 电影、电视剧
|
||||||
type: Optional[str] = None
|
type: Optional[str] = None
|
||||||
# 媒体标题
|
# 媒体标题
|
||||||
@ -228,3 +230,39 @@ class Context(BaseModel):
|
|||||||
media_info: Optional[MediaInfo] = None
|
media_info: Optional[MediaInfo] = None
|
||||||
# 种子信息
|
# 种子信息
|
||||||
torrent_info: Optional[TorrentInfo] = None
|
torrent_info: Optional[TorrentInfo] = None
|
||||||
|
|
||||||
|
|
||||||
|
class MediaPerson(BaseModel):
|
||||||
|
"""
|
||||||
|
媒体人物信息
|
||||||
|
"""
|
||||||
|
# 来源:themoviedb、douban、bangumi
|
||||||
|
source: Optional[str] = None
|
||||||
|
# 公共
|
||||||
|
id: Optional[int] = None
|
||||||
|
type: Optional[Union[str, int]] = 1
|
||||||
|
name: Optional[str] = None
|
||||||
|
character: Optional[str] = None
|
||||||
|
images: Optional[dict] = {}
|
||||||
|
# themoviedb
|
||||||
|
profile_path: Optional[str] = None
|
||||||
|
gender: Optional[Union[str, int]] = None
|
||||||
|
original_name: Optional[str] = None
|
||||||
|
credit_id: Optional[str] = None
|
||||||
|
also_known_as: Optional[list] = []
|
||||||
|
birthday: Optional[str] = None
|
||||||
|
deathday: Optional[str] = None
|
||||||
|
imdb_id: Optional[str] = None
|
||||||
|
known_for_department: Optional[str] = None
|
||||||
|
place_of_birth: Optional[str] = None
|
||||||
|
popularity: Optional[float] = None
|
||||||
|
biography: Optional[str] = None
|
||||||
|
# douban
|
||||||
|
roles: Optional[list] = []
|
||||||
|
title: Optional[str] = None
|
||||||
|
url: Optional[str] = None
|
||||||
|
avatar: Optional[Union[str, dict]] = None
|
||||||
|
latin_name: Optional[str] = None
|
||||||
|
# bangumi
|
||||||
|
career: Optional[list] = []
|
||||||
|
relation: Optional[str] = None
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
from typing import Optional
|
|
||||||
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
|
|
||||||
class DoubanPerson(BaseModel):
|
|
||||||
id: Optional[str] = None
|
|
||||||
name: Optional[str] = None
|
|
||||||
roles: Optional[list] = []
|
|
||||||
title: Optional[str] = None
|
|
||||||
url: Optional[str] = None
|
|
||||||
character: Optional[str] = None
|
|
||||||
avatar: Optional[dict] = None
|
|
||||||
latin_name: Optional[str] = None
|
|
@ -30,22 +30,3 @@ class TmdbEpisode(BaseModel):
|
|||||||
vote_average: Optional[float] = None
|
vote_average: Optional[float] = None
|
||||||
crew: Optional[list] = []
|
crew: Optional[list] = []
|
||||||
guest_stars: Optional[list] = []
|
guest_stars: Optional[list] = []
|
||||||
|
|
||||||
|
|
||||||
class TmdbPerson(BaseModel):
|
|
||||||
id: Optional[int] = None
|
|
||||||
name: Optional[str] = None
|
|
||||||
character: Optional[str] = None
|
|
||||||
profile_path: Optional[str] = None
|
|
||||||
gender: Optional[int] = None
|
|
||||||
original_name: Optional[str] = None
|
|
||||||
credit_id: Optional[str] = None
|
|
||||||
also_known_as: Optional[list] = []
|
|
||||||
birthday: Optional[str] = None
|
|
||||||
deathday: Optional[str] = None
|
|
||||||
imdb_id: Optional[str] = None
|
|
||||||
known_for_department: Optional[str] = None
|
|
||||||
place_of_birth: Optional[str] = None
|
|
||||||
popularity: Optional[float] = None
|
|
||||||
images: Optional[dict] = {}
|
|
||||||
biography: Optional[str] = None
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user