add Bangumi
This commit is contained in:
parent
f7c1d28c0f
commit
b6486035c4
@ -1,7 +1,8 @@
|
|||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
|
||||||
from app.api.endpoints import login, user, site, message, webhook, subscribe, \
|
from app.api.endpoints import login, user, site, message, webhook, subscribe, \
|
||||||
media, douban, search, plugin, tmdb, history, system, download, dashboard, filebrowser, transfer, mediaserver
|
media, douban, search, plugin, tmdb, history, system, download, dashboard, \
|
||||||
|
filebrowser, transfer, mediaserver, bangumi
|
||||||
|
|
||||||
api_router = APIRouter()
|
api_router = APIRouter()
|
||||||
api_router.include_router(login.router, prefix="/login", tags=["login"])
|
api_router.include_router(login.router, prefix="/login", tags=["login"])
|
||||||
@ -22,3 +23,5 @@ api_router.include_router(dashboard.router, prefix="/dashboard", tags=["dashboar
|
|||||||
api_router.include_router(filebrowser.router, prefix="/filebrowser", tags=["filebrowser"])
|
api_router.include_router(filebrowser.router, prefix="/filebrowser", tags=["filebrowser"])
|
||||||
api_router.include_router(transfer.router, prefix="/transfer", tags=["transfer"])
|
api_router.include_router(transfer.router, prefix="/transfer", tags=["transfer"])
|
||||||
api_router.include_router(mediaserver.router, prefix="/mediaserver", tags=["mediaserver"])
|
api_router.include_router(mediaserver.router, prefix="/mediaserver", tags=["mediaserver"])
|
||||||
|
api_router.include_router(bangumi.router, prefix="/bangumi", tags=["bangumi"])
|
||||||
|
|
||||||
|
64
app/api/endpoints/bangumi.py
Normal file
64
app/api/endpoints/bangumi.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
from typing import List, Any
|
||||||
|
|
||||||
|
from fastapi import APIRouter, Depends
|
||||||
|
|
||||||
|
from app import schemas
|
||||||
|
from app.chain.bangumi import BangumiChain
|
||||||
|
from app.core.context import MediaInfo
|
||||||
|
from app.core.security import verify_token
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/calendar", summary="Bangumi每日放送", response_model=List[schemas.MediaInfo])
|
||||||
|
def calendar(page: int = 1,
|
||||||
|
count: int = 30,
|
||||||
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
|
"""
|
||||||
|
浏览Bangumi每日放送
|
||||||
|
"""
|
||||||
|
infos = BangumiChain().calendar(page=page, count=count)
|
||||||
|
if not infos:
|
||||||
|
return []
|
||||||
|
medias = [MediaInfo(bangumi_info=info) for info in infos]
|
||||||
|
return [media.to_dict() for media in medias]
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/credits/{bangumiid}", summary="查询Bangumi演职员表", response_model=List[schemas.BangumiPerson])
|
||||||
|
def bangumi_credits(bangumiid: int,
|
||||||
|
page: int = 1,
|
||||||
|
count: int = 20,
|
||||||
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
|
"""
|
||||||
|
查询Bangumi演职员表
|
||||||
|
"""
|
||||||
|
persons = BangumiChain().bangumi_credits(bangumiid, page=page, count=count)
|
||||||
|
if not persons:
|
||||||
|
return []
|
||||||
|
return [schemas.BangumiPerson(**person) for person in persons]
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/recommend/{bangumiid}", summary="查询Bangumi推荐", response_model=List[schemas.MediaInfo])
|
||||||
|
def bangumi_recommend(bangumiid: int,
|
||||||
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
|
"""
|
||||||
|
查询Bangumi推荐
|
||||||
|
"""
|
||||||
|
infos = BangumiChain().bangumi_recommend(bangumiid)
|
||||||
|
if not infos:
|
||||||
|
return []
|
||||||
|
medias = [MediaInfo(bangumi_info=info) for info in infos]
|
||||||
|
return [media.to_dict() for media in medias]
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{bangumiid}", summary="查询Bangumi详情", response_model=schemas.MediaInfo)
|
||||||
|
def bangumi_info(bangumiid: int,
|
||||||
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
|
"""
|
||||||
|
查询Bangumi详情
|
||||||
|
"""
|
||||||
|
info = BangumiChain().bangumi_info(bangumiid)
|
||||||
|
if info:
|
||||||
|
return MediaInfo(bangumi_info=info).to_dict()
|
||||||
|
else:
|
||||||
|
return schemas.MediaInfo()
|
@ -106,14 +106,17 @@ def media_info(mediaid: str, type_name: str,
|
|||||||
根据媒体ID查询themoviedb或豆瓣媒体信息,type_name: 电影/电视剧
|
根据媒体ID查询themoviedb或豆瓣媒体信息,type_name: 电影/电视剧
|
||||||
"""
|
"""
|
||||||
mtype = MediaType(type_name)
|
mtype = MediaType(type_name)
|
||||||
tmdbid, doubanid = None, None
|
tmdbid, doubanid, bangumiid = None, None, None
|
||||||
if mediaid.startswith("tmdb:"):
|
if mediaid.startswith("tmdb:"):
|
||||||
tmdbid = int(mediaid[5:])
|
tmdbid = int(mediaid[5:])
|
||||||
elif mediaid.startswith("douban:"):
|
elif mediaid.startswith("douban:"):
|
||||||
doubanid = mediaid[7:]
|
doubanid = mediaid[7:]
|
||||||
if not tmdbid and not doubanid:
|
elif mediaid.startswith("bangumi:"):
|
||||||
|
bangumiid = int(mediaid[8:])
|
||||||
|
if not tmdbid and not doubanid and not bangumiid:
|
||||||
return schemas.MediaInfo()
|
return schemas.MediaInfo()
|
||||||
mediainfo = MediaChain().recognize_media(tmdbid=tmdbid, doubanid=doubanid, mtype=mtype)
|
# 识别
|
||||||
|
mediainfo = MediaChain().recognize_media(tmdbid=tmdbid, doubanid=doubanid, bangumiid=bangumiid, mtype=mtype)
|
||||||
if mediainfo:
|
if mediainfo:
|
||||||
MediaChain().obtain_images(mediainfo)
|
MediaChain().obtain_images(mediainfo)
|
||||||
return mediainfo.to_dict()
|
return mediainfo.to_dict()
|
||||||
|
@ -52,6 +52,20 @@ def search_by_id(mediaid: str,
|
|||||||
mtype=mtype, area=area)
|
mtype=mtype, area=area)
|
||||||
else:
|
else:
|
||||||
torrents = SearchChain().search_by_id(doubanid=doubanid, mtype=mtype, area=area)
|
torrents = SearchChain().search_by_id(doubanid=doubanid, mtype=mtype, area=area)
|
||||||
|
elif mediaid.startswith("bangumi:"):
|
||||||
|
bangumiid = int(mediaid.replace("bangumi:", ""))
|
||||||
|
if settings.RECOGNIZE_SOURCE == "themoviedb":
|
||||||
|
# 通过BangumiID识别TMDBID
|
||||||
|
tmdbinfo = MediaChain().get_tmdbinfo_by_bangumiid(bangumiid=bangumiid)
|
||||||
|
if tmdbinfo:
|
||||||
|
torrents = SearchChain().search_by_id(tmdbid=tmdbinfo.get("id"),
|
||||||
|
mtype=mtype, area=area)
|
||||||
|
else:
|
||||||
|
# 通过BangumiID识别豆瓣ID
|
||||||
|
doubaninfo = MediaChain().get_doubaninfo_by_bangumiid(bangumiid=bangumiid)
|
||||||
|
if doubaninfo:
|
||||||
|
torrents = SearchChain().search_by_id(doubanid=doubaninfo.get("id"),
|
||||||
|
mtype=mtype, area=area)
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
return [torrent.to_dict() for torrent in torrents]
|
return [torrent.to_dict() for torrent in torrents]
|
||||||
|
@ -65,7 +65,7 @@ def create_subscribe(
|
|||||||
else:
|
else:
|
||||||
mtype = None
|
mtype = None
|
||||||
# 豆瓣标理
|
# 豆瓣标理
|
||||||
if subscribe_in.doubanid:
|
if subscribe_in.doubanid or subscribe_in.bangumiid:
|
||||||
meta = MetaInfo(subscribe_in.name)
|
meta = MetaInfo(subscribe_in.name)
|
||||||
subscribe_in.name = meta.name
|
subscribe_in.name = meta.name
|
||||||
subscribe_in.season = meta.begin_season
|
subscribe_in.season = meta.begin_season
|
||||||
@ -80,6 +80,7 @@ def create_subscribe(
|
|||||||
tmdbid=subscribe_in.tmdbid,
|
tmdbid=subscribe_in.tmdbid,
|
||||||
season=subscribe_in.season,
|
season=subscribe_in.season,
|
||||||
doubanid=subscribe_in.doubanid,
|
doubanid=subscribe_in.doubanid,
|
||||||
|
bangumiid=subscribe_in.bangumiid,
|
||||||
username=current_user.name,
|
username=current_user.name,
|
||||||
best_version=subscribe_in.best_version,
|
best_version=subscribe_in.best_version,
|
||||||
save_path=subscribe_in.save_path,
|
save_path=subscribe_in.save_path,
|
||||||
@ -131,9 +132,10 @@ def subscribe_mediaid(
|
|||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||||
"""
|
"""
|
||||||
根据TMDBID或豆瓣ID查询订阅 tmdb:/douban:
|
根据 TMDBID/豆瓣ID/BangumiId 查询订阅 tmdb:/douban:
|
||||||
"""
|
"""
|
||||||
result = None
|
result = None
|
||||||
|
title_check = False
|
||||||
if mediaid.startswith("tmdb:"):
|
if mediaid.startswith("tmdb:"):
|
||||||
tmdbid = mediaid[5:]
|
tmdbid = mediaid[5:]
|
||||||
if not tmdbid or not str(tmdbid).isdigit():
|
if not tmdbid or not str(tmdbid).isdigit():
|
||||||
@ -144,14 +146,21 @@ def subscribe_mediaid(
|
|||||||
if not doubanid:
|
if not doubanid:
|
||||||
return Subscribe()
|
return Subscribe()
|
||||||
result = Subscribe.get_by_doubanid(db, doubanid)
|
result = Subscribe.get_by_doubanid(db, doubanid)
|
||||||
# 豆瓣已订阅如果 id 搜索无结果使用标题搜索
|
|
||||||
# 会造成同名结果也会被返回
|
|
||||||
if not result and title:
|
if not result and title:
|
||||||
meta = MetaInfo(title)
|
title_check = True
|
||||||
if season:
|
elif mediaid.startswith("bangumi:"):
|
||||||
meta.begin_season = season
|
bangumiid = mediaid[8:]
|
||||||
result = Subscribe.get_by_title(db, title=meta.name, season=meta.begin_season)
|
if not bangumiid or not str(bangumiid).isdigit():
|
||||||
|
return Subscribe()
|
||||||
|
result = Subscribe.get_by_bangumiid(db, int(bangumiid))
|
||||||
|
if not result and title:
|
||||||
|
title_check = True
|
||||||
|
# 使用名称检查订阅
|
||||||
|
if title_check and title:
|
||||||
|
meta = MetaInfo(title)
|
||||||
|
if season:
|
||||||
|
meta.begin_season = season
|
||||||
|
result = Subscribe.get_by_title(db, title=meta.name, season=meta.begin_season)
|
||||||
if result and result.sites:
|
if result and result.sites:
|
||||||
result.sites = json.loads(result.sites)
|
result.sites = json.loads(result.sites)
|
||||||
|
|
||||||
|
@ -119,6 +119,7 @@ class ChainBase(metaclass=ABCMeta):
|
|||||||
mtype: MediaType = None,
|
mtype: MediaType = None,
|
||||||
tmdbid: int = None,
|
tmdbid: int = None,
|
||||||
doubanid: str = None,
|
doubanid: str = None,
|
||||||
|
bangumiid: int = None,
|
||||||
cache: bool = True) -> Optional[MediaInfo]:
|
cache: bool = True) -> Optional[MediaInfo]:
|
||||||
"""
|
"""
|
||||||
识别媒体信息
|
识别媒体信息
|
||||||
@ -126,6 +127,7 @@ class ChainBase(metaclass=ABCMeta):
|
|||||||
:param mtype: 识别的媒体类型,与tmdbid配套
|
:param mtype: 识别的媒体类型,与tmdbid配套
|
||||||
:param tmdbid: tmdbid
|
:param tmdbid: tmdbid
|
||||||
:param doubanid: 豆瓣ID
|
:param doubanid: 豆瓣ID
|
||||||
|
:param bangumiid: BangumiID
|
||||||
:param cache: 是否使用缓存
|
:param cache: 是否使用缓存
|
||||||
:return: 识别的媒体信息,包括剧集信息
|
:return: 识别的媒体信息,包括剧集信息
|
||||||
"""
|
"""
|
||||||
@ -136,11 +138,12 @@ class ChainBase(metaclass=ABCMeta):
|
|||||||
tmdbid = meta.tmdbid
|
tmdbid = meta.tmdbid
|
||||||
if not doubanid and hasattr(meta, "doubanid"):
|
if not doubanid and hasattr(meta, "doubanid"):
|
||||||
doubanid = meta.doubanid
|
doubanid = meta.doubanid
|
||||||
# 有tmdbid时不使用doubanid
|
# 有tmdbid时不使用其它ID
|
||||||
if tmdbid:
|
if tmdbid:
|
||||||
doubanid = None
|
doubanid = None
|
||||||
|
bangumiid = None
|
||||||
return self.run_module("recognize_media", meta=meta, mtype=mtype,
|
return self.run_module("recognize_media", meta=meta, mtype=mtype,
|
||||||
tmdbid=tmdbid, doubanid=doubanid, cache=cache)
|
tmdbid=tmdbid, doubanid=doubanid, bangumiid=bangumiid, cache=cache)
|
||||||
|
|
||||||
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) -> Optional[dict]:
|
mtype: MediaType = None, year: str = None, season: int = None) -> Optional[dict]:
|
||||||
@ -217,6 +220,14 @@ class ChainBase(metaclass=ABCMeta):
|
|||||||
"""
|
"""
|
||||||
return self.run_module("tmdb_info", tmdbid=tmdbid, mtype=mtype)
|
return self.run_module("tmdb_info", tmdbid=tmdbid, mtype=mtype)
|
||||||
|
|
||||||
|
def bangumi_info(self, bangumiid: int) -> Optional[dict]:
|
||||||
|
"""
|
||||||
|
获取Bangumi信息
|
||||||
|
:param bangumiid: int
|
||||||
|
:return: Bangumi信息
|
||||||
|
"""
|
||||||
|
return self.run_module("bangumi_info", bangumiid=bangumiid)
|
||||||
|
|
||||||
def message_parser(self, body: Any, form: Any,
|
def message_parser(self, body: Any, form: Any,
|
||||||
args: Any) -> Optional[CommingMessage]:
|
args: Any) -> Optional[CommingMessage]:
|
||||||
"""
|
"""
|
||||||
|
42
app/chain/bangumi.py
Normal file
42
app/chain/bangumi.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
from typing import Optional, List
|
||||||
|
|
||||||
|
from app.chain import ChainBase
|
||||||
|
from app.utils.singleton import Singleton
|
||||||
|
|
||||||
|
|
||||||
|
class BangumiChain(ChainBase, metaclass=Singleton):
|
||||||
|
"""
|
||||||
|
Bangumi处理链,单例运行
|
||||||
|
"""
|
||||||
|
|
||||||
|
def calendar(self, page: int = 1, count: int = 30) -> Optional[List[dict]]:
|
||||||
|
"""
|
||||||
|
获取Bangumi每日放送
|
||||||
|
:param page: 页码
|
||||||
|
:param count: 每页数量
|
||||||
|
"""
|
||||||
|
return self.run_module("bangumi_calendar", page=page, count=count)
|
||||||
|
|
||||||
|
def bangumi_info(self, bangumiid: int) -> Optional[dict]:
|
||||||
|
"""
|
||||||
|
获取Bangumi信息
|
||||||
|
:param bangumiid: BangumiID
|
||||||
|
:return: Bangumi信息
|
||||||
|
"""
|
||||||
|
return self.run_module("bangumi_info", bangumiid=bangumiid)
|
||||||
|
|
||||||
|
def bangumi_credits(self, bangumiid: int, page: int = 1, count: int = 20) -> List[dict]:
|
||||||
|
"""
|
||||||
|
根据BangumiID查询电影演职员表
|
||||||
|
:param bangumiid: BangumiID
|
||||||
|
:param page: 页码
|
||||||
|
:param count: 数量
|
||||||
|
"""
|
||||||
|
return self.run_module("bangumi_credits", bangumiid=bangumiid, page=page, count=count)
|
||||||
|
|
||||||
|
def bangumi_recommend(self, bangumiid: int) -> List[dict]:
|
||||||
|
"""
|
||||||
|
根据BangumiID查询推荐电影
|
||||||
|
:param bangumiid: BangumiID
|
||||||
|
"""
|
||||||
|
return self.run_module("bangumi_recommend", bangumiid=bangumiid)
|
@ -229,6 +229,28 @@ class MediaChain(ChainBase, metaclass=Singleton):
|
|||||||
)
|
)
|
||||||
return tmdbinfo
|
return tmdbinfo
|
||||||
|
|
||||||
|
def get_tmdbinfo_by_bangumiid(self, bangumiid: int) -> Optional[dict]:
|
||||||
|
"""
|
||||||
|
根据BangumiID获取TMDB信息
|
||||||
|
"""
|
||||||
|
bangumiinfo = self.bangumi_info(bangumiid=bangumiid)
|
||||||
|
if bangumiinfo:
|
||||||
|
# 名称
|
||||||
|
name = bangumiinfo.get("name") or bangumiinfo.get("name_cn")
|
||||||
|
# 年份
|
||||||
|
release_date = bangumiinfo.get("date") or bangumiinfo.get("air_date")
|
||||||
|
if release_date:
|
||||||
|
year = release_date[:4]
|
||||||
|
else:
|
||||||
|
year = None
|
||||||
|
# 使用名称识别TMDB媒体信息
|
||||||
|
return self.match_tmdbinfo(
|
||||||
|
name=name,
|
||||||
|
year=year,
|
||||||
|
mtype=MediaType.TV
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
def get_doubaninfo_by_tmdbid(self, tmdbid: int,
|
def get_doubaninfo_by_tmdbid(self, tmdbid: int,
|
||||||
mtype: MediaType = None, season: int = None) -> Optional[dict]:
|
mtype: MediaType = None, season: int = None) -> Optional[dict]:
|
||||||
"""
|
"""
|
||||||
@ -261,3 +283,25 @@ class MediaChain(ChainBase, metaclass=Singleton):
|
|||||||
imdbid=imdbid
|
imdbid=imdbid
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def get_doubaninfo_by_bangumiid(self, bangumiid: int) -> Optional[dict]:
|
||||||
|
"""
|
||||||
|
根据BangumiID获取豆瓣信息
|
||||||
|
"""
|
||||||
|
bangumiinfo = self.bangumi_info(bangumiid=bangumiid)
|
||||||
|
if bangumiinfo:
|
||||||
|
# 名称
|
||||||
|
name = bangumiinfo.get("name") or bangumiinfo.get("name_cn")
|
||||||
|
# 年份
|
||||||
|
release_date = bangumiinfo.get("date") or bangumiinfo.get("air_date")
|
||||||
|
if release_date:
|
||||||
|
year = release_date[:4]
|
||||||
|
else:
|
||||||
|
year = None
|
||||||
|
# 使用名称识别豆瓣媒体信息
|
||||||
|
return self.match_doubaninfo(
|
||||||
|
name=name,
|
||||||
|
year=year,
|
||||||
|
mtype=MediaType.TV
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
@ -45,6 +45,7 @@ class SubscribeChain(ChainBase):
|
|||||||
mtype: MediaType = None,
|
mtype: MediaType = None,
|
||||||
tmdbid: int = None,
|
tmdbid: int = None,
|
||||||
doubanid: str = None,
|
doubanid: str = None,
|
||||||
|
bangumiid: int = None,
|
||||||
season: int = None,
|
season: int = None,
|
||||||
channel: MessageChannel = None,
|
channel: MessageChannel = None,
|
||||||
userid: str = None,
|
userid: str = None,
|
||||||
@ -100,6 +101,7 @@ class SubscribeChain(ChainBase):
|
|||||||
mediainfo = self.recognize_media(mtype=mediainfo.type,
|
mediainfo = self.recognize_media(mtype=mediainfo.type,
|
||||||
tmdbid=mediainfo.tmdb_id,
|
tmdbid=mediainfo.tmdb_id,
|
||||||
doubanid=mediainfo.douban_id,
|
doubanid=mediainfo.douban_id,
|
||||||
|
bangumiid=mediainfo.bangumi_id,
|
||||||
cache=False)
|
cache=False)
|
||||||
if not mediainfo:
|
if not mediainfo:
|
||||||
logger.error(f"媒体信息识别失败!")
|
logger.error(f"媒体信息识别失败!")
|
||||||
@ -124,6 +126,8 @@ class SubscribeChain(ChainBase):
|
|||||||
# 合并信息
|
# 合并信息
|
||||||
if doubanid:
|
if doubanid:
|
||||||
mediainfo.douban_id = doubanid
|
mediainfo.douban_id = doubanid
|
||||||
|
if bangumiid:
|
||||||
|
mediainfo.bangumi_id = bangumiid
|
||||||
# 添加订阅
|
# 添加订阅
|
||||||
sid, err_msg = self.subscribeoper.add(mediainfo, season=season, username=username, **kwargs)
|
sid, err_msg = self.subscribeoper.add(mediainfo, season=season, username=username, **kwargs)
|
||||||
if not sid:
|
if not sid:
|
||||||
|
@ -153,6 +153,8 @@ class MediaInfo:
|
|||||||
tvdb_id: int = None
|
tvdb_id: int = None
|
||||||
# 豆瓣ID
|
# 豆瓣ID
|
||||||
douban_id: str = None
|
douban_id: str = None
|
||||||
|
# Bangumi ID
|
||||||
|
bangumi_id: int = None
|
||||||
# 媒体原语种
|
# 媒体原语种
|
||||||
original_language: str = None
|
original_language: str = None
|
||||||
# 媒体原发行标题
|
# 媒体原发行标题
|
||||||
@ -185,6 +187,8 @@ class MediaInfo:
|
|||||||
tmdb_info: dict = field(default_factory=dict)
|
tmdb_info: dict = field(default_factory=dict)
|
||||||
# 豆瓣 INFO
|
# 豆瓣 INFO
|
||||||
douban_info: dict = field(default_factory=dict)
|
douban_info: dict = field(default_factory=dict)
|
||||||
|
# Bangumi INFO
|
||||||
|
bangumi_info: dict = field(default_factory=dict)
|
||||||
# 导演
|
# 导演
|
||||||
directors: List[dict] = field(default_factory=list)
|
directors: List[dict] = field(default_factory=list)
|
||||||
# 演员
|
# 演员
|
||||||
@ -240,6 +244,8 @@ class MediaInfo:
|
|||||||
self.set_tmdb_info(self.tmdb_info)
|
self.set_tmdb_info(self.tmdb_info)
|
||||||
if self.douban_info:
|
if self.douban_info:
|
||||||
self.set_douban_info(self.douban_info)
|
self.set_douban_info(self.douban_info)
|
||||||
|
if self.bangumi_info:
|
||||||
|
self.set_bangumi_info(self.bangumi_info)
|
||||||
|
|
||||||
def __setattr__(self, name: str, value: Any):
|
def __setattr__(self, name: str, value: Any):
|
||||||
self.__dict__[name] = value
|
self.__dict__[name] = value
|
||||||
@ -540,6 +546,69 @@ class MediaInfo:
|
|||||||
if not hasattr(self, key):
|
if not hasattr(self, key):
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
|
|
||||||
|
def set_bangumi_info(self, info: dict):
|
||||||
|
"""
|
||||||
|
初始化Bangumi信息
|
||||||
|
"""
|
||||||
|
if not info:
|
||||||
|
return
|
||||||
|
# 本体
|
||||||
|
self.bangumi_info = info
|
||||||
|
# 豆瓣ID
|
||||||
|
self.bangumi_id = info.get("id")
|
||||||
|
# 类型
|
||||||
|
if not self.type:
|
||||||
|
self.type = MediaType.TV
|
||||||
|
# 标题
|
||||||
|
if not self.title:
|
||||||
|
self.title = info.get("name_cn") or info.get("name")
|
||||||
|
# 原语种标题
|
||||||
|
if not self.original_title:
|
||||||
|
self.original_title = info.get("name")
|
||||||
|
# 识别标题中的季
|
||||||
|
meta = MetaInfo(self.title)
|
||||||
|
# 季
|
||||||
|
if not self.season:
|
||||||
|
self.season = meta.begin_season
|
||||||
|
# 评分
|
||||||
|
if not self.vote_average:
|
||||||
|
rating = info.get("rating")
|
||||||
|
if rating:
|
||||||
|
vote_average = float(rating.get("score"))
|
||||||
|
else:
|
||||||
|
vote_average = 0
|
||||||
|
self.vote_average = vote_average
|
||||||
|
# 发行日期
|
||||||
|
if not self.release_date:
|
||||||
|
self.release_date = info.get("date") or info.get("air_date")
|
||||||
|
# 年份
|
||||||
|
if not self.year:
|
||||||
|
self.year = self.release_date[:4] if self.release_date else None
|
||||||
|
# 海报
|
||||||
|
if not self.poster_path:
|
||||||
|
self.poster_path = info.get("images", {}).get("large")
|
||||||
|
# 简介
|
||||||
|
if not self.overview:
|
||||||
|
self.overview = info.get("summary")
|
||||||
|
# 别名
|
||||||
|
if not self.names:
|
||||||
|
infobox = info.get("infobox")
|
||||||
|
if infobox:
|
||||||
|
akas = [item.get("value") for item in infobox if item.get("key") == "别名"]
|
||||||
|
if akas:
|
||||||
|
self.names = [aka.get("v") for aka in akas[0]]
|
||||||
|
|
||||||
|
# 剧集
|
||||||
|
if self.type == MediaType.TV and not self.seasons:
|
||||||
|
meta = MetaInfo(self.title)
|
||||||
|
season = meta.begin_season or 1
|
||||||
|
episodes_count = info.get("total_episodes")
|
||||||
|
if episodes_count:
|
||||||
|
self.seasons[season] = list(range(1, episodes_count + 1))
|
||||||
|
# 演员
|
||||||
|
if not self.actors:
|
||||||
|
self.actors = info.get("actors") or []
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def title_year(self):
|
def title_year(self):
|
||||||
if self.title:
|
if self.title:
|
||||||
@ -558,6 +627,8 @@ class MediaInfo:
|
|||||||
return "https://www.themoviedb.org/tv/%s" % self.tmdb_id
|
return "https://www.themoviedb.org/tv/%s" % self.tmdb_id
|
||||||
elif self.douban_id:
|
elif self.douban_id:
|
||||||
return "https://movie.douban.com/subject/%s" % self.douban_id
|
return "https://movie.douban.com/subject/%s" % self.douban_id
|
||||||
|
elif self.bangumi_id:
|
||||||
|
return "http://bgm.tv/subject/%s" % self.bangumi_id
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -621,6 +692,7 @@ class MediaInfo:
|
|||||||
dicts["title_year"] = self.title_year
|
dicts["title_year"] = self.title_year
|
||||||
dicts["tmdb_info"] = None
|
dicts["tmdb_info"] = None
|
||||||
dicts["douban_info"] = None
|
dicts["douban_info"] = None
|
||||||
|
dicts["bangumi_info"] = None
|
||||||
return dicts
|
return dicts
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
@ -629,6 +701,7 @@ class MediaInfo:
|
|||||||
"""
|
"""
|
||||||
self.tmdb_info = {}
|
self.tmdb_info = {}
|
||||||
self.douban_info = {}
|
self.douban_info = {}
|
||||||
|
self.bangumi_info = {}
|
||||||
self.seasons = {}
|
self.seasons = {}
|
||||||
self.genres = []
|
self.genres = []
|
||||||
self.season_info = []
|
self.season_info = []
|
||||||
|
@ -69,8 +69,8 @@ class MetaBase(object):
|
|||||||
_subtitle_flag = False
|
_subtitle_flag = False
|
||||||
_subtitle_season_re = r"(?<![全共]\s*)[第\s]+([0-9一二三四五六七八九十S\-]+)\s*季(?!\s*[全共])"
|
_subtitle_season_re = r"(?<![全共]\s*)[第\s]+([0-9一二三四五六七八九十S\-]+)\s*季(?!\s*[全共])"
|
||||||
_subtitle_season_all_re = r"[全共]\s*([0-9一二三四五六七八九十]+)\s*季|([0-9一二三四五六七八九十]+)\s*季\s*全"
|
_subtitle_season_all_re = r"[全共]\s*([0-9一二三四五六七八九十]+)\s*季|([0-9一二三四五六七八九十]+)\s*季\s*全"
|
||||||
_subtitle_episode_re = r"(?<![全共]\s*)[第\s]+([0-9一二三四五六七八九十百零EP\-]+)\s*[集话話期](?!\s*[全共])"
|
_subtitle_episode_re = r"(?<![全共]\s*)[第\s]+([0-9一二三四五六七八九十百零EP\-]+)\s*[集话話期幕](?!\s*[全共])"
|
||||||
_subtitle_episode_all_re = r"([0-9一二三四五六七八九十百零]+)\s*集\s*全|[全共]\s*([0-9一二三四五六七八九十百零]+)\s*[集话話期]"
|
_subtitle_episode_all_re = r"([0-9一二三四五六七八九十百零]+)\s*集\s*全|[全共]\s*([0-9一二三四五六七八九十百零]+)\s*[集话話期幕]"
|
||||||
|
|
||||||
def __init__(self, title: str, subtitle: str = None, isfile: bool = False):
|
def __init__(self, title: str, subtitle: str = None, isfile: bool = False):
|
||||||
if not title:
|
if not title:
|
||||||
@ -110,7 +110,7 @@ class MetaBase(object):
|
|||||||
if not title_text:
|
if not title_text:
|
||||||
return
|
return
|
||||||
title_text = f" {title_text} "
|
title_text = f" {title_text} "
|
||||||
if re.search(r'[全第季集话話期]', title_text, re.IGNORECASE):
|
if re.search(r'[全第季集话話期幕]', title_text, re.IGNORECASE):
|
||||||
# 第x季
|
# 第x季
|
||||||
season_str = re.search(r'%s' % self._subtitle_season_re, title_text, re.IGNORECASE)
|
season_str = re.search(r'%s' % self._subtitle_season_re, title_text, re.IGNORECASE)
|
||||||
if season_str:
|
if season_str:
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
from typing import Optional
|
from typing import Optional, Union
|
||||||
|
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ class MessageOper(DbOper):
|
|||||||
link: str = None,
|
link: str = None,
|
||||||
userid: str = None,
|
userid: str = None,
|
||||||
action: int = 1,
|
action: int = 1,
|
||||||
note: dict = None,
|
note: Union[list, dict] = None,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
"""
|
"""
|
||||||
新增媒体服务器数据
|
新增媒体服务器数据
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
from sqlalchemy import Column, Integer, String, Sequence
|
from sqlalchemy import Column, Integer, String, Sequence, Float
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from app.db import db_query, db_update, Base
|
from app.db import db_query, db_update, Base
|
||||||
@ -23,14 +23,15 @@ class Subscribe(Base):
|
|||||||
imdbid = Column(String)
|
imdbid = Column(String)
|
||||||
tvdbid = Column(Integer)
|
tvdbid = Column(Integer)
|
||||||
doubanid = Column(String, index=True)
|
doubanid = Column(String, index=True)
|
||||||
|
bangumiid = Column(Integer, index=True)
|
||||||
# 季号
|
# 季号
|
||||||
season = Column(Integer)
|
season = Column(Integer)
|
||||||
# 海报
|
# 海报
|
||||||
poster = Column(String)
|
poster = Column(String)
|
||||||
# 背景图
|
# 背景图
|
||||||
backdrop = Column(String)
|
backdrop = Column(String)
|
||||||
# 评分
|
# 评分,float
|
||||||
vote = Column(Integer)
|
vote = Column(Float)
|
||||||
# 简介
|
# 简介
|
||||||
description = Column(String)
|
description = Column(String)
|
||||||
# 过滤规则
|
# 过滤规则
|
||||||
@ -115,6 +116,11 @@ class Subscribe(Base):
|
|||||||
def get_by_doubanid(db: Session, doubanid: str):
|
def get_by_doubanid(db: Session, doubanid: str):
|
||||||
return db.query(Subscribe).filter(Subscribe.doubanid == doubanid).first()
|
return db.query(Subscribe).filter(Subscribe.doubanid == doubanid).first()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@db_query
|
||||||
|
def get_by_bangumiid(db: Session, bangumiid: int):
|
||||||
|
return db.query(Subscribe).filter(Subscribe.bangumiid == bangumiid).first()
|
||||||
|
|
||||||
@db_update
|
@db_update
|
||||||
def delete_by_tmdbid(self, db: Session, tmdbid: int, season: int):
|
def delete_by_tmdbid(self, db: Session, tmdbid: int, season: int):
|
||||||
subscrbies = self.get_by_tmdbid(db, tmdbid, season)
|
subscrbies = self.get_by_tmdbid(db, tmdbid, season)
|
||||||
|
@ -27,6 +27,7 @@ class SubscribeOper(DbOper):
|
|||||||
imdbid=mediainfo.imdb_id,
|
imdbid=mediainfo.imdb_id,
|
||||||
tvdbid=mediainfo.tvdb_id,
|
tvdbid=mediainfo.tvdb_id,
|
||||||
doubanid=mediainfo.douban_id,
|
doubanid=mediainfo.douban_id,
|
||||||
|
bangumiid=mediainfo.bangumi_id,
|
||||||
poster=mediainfo.get_poster_image(),
|
poster=mediainfo.get_poster_image(),
|
||||||
backdrop=mediainfo.get_backdrop_image(),
|
backdrop=mediainfo.get_backdrop_image(),
|
||||||
vote=mediainfo.vote_average,
|
vote=mediainfo.vote_average,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import json
|
import json
|
||||||
import queue
|
import queue
|
||||||
import time
|
import time
|
||||||
from typing import Optional, Any
|
from typing import Optional, Any, Union
|
||||||
|
|
||||||
from app.utils.singleton import Singleton
|
from app.utils.singleton import Singleton
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ class MessageHelper(metaclass=Singleton):
|
|||||||
self.sys_queue = queue.Queue()
|
self.sys_queue = queue.Queue()
|
||||||
self.user_queue = queue.Queue()
|
self.user_queue = queue.Queue()
|
||||||
|
|
||||||
def put(self, message: Any, role: str = "sys", note: dict = None):
|
def put(self, message: Any, role: str = "sys", note: Union[list, dict] = None):
|
||||||
"""
|
"""
|
||||||
存消息
|
存消息
|
||||||
:param message: 消息
|
:param message: 消息
|
||||||
|
93
app/modules/bangumi/__init__.py
Normal file
93
app/modules/bangumi/__init__.py
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
from typing import List, Optional, Tuple, Union
|
||||||
|
|
||||||
|
from app.core.context import MediaInfo
|
||||||
|
from app.log import logger
|
||||||
|
from app.modules import _ModuleBase
|
||||||
|
from app.modules.bangumi.bangumi import BangumiApi
|
||||||
|
from app.utils.http import RequestUtils
|
||||||
|
|
||||||
|
|
||||||
|
class BangumiModule(_ModuleBase):
|
||||||
|
bangumiapi: BangumiApi = None
|
||||||
|
|
||||||
|
def init_module(self) -> None:
|
||||||
|
self.bangumiapi = BangumiApi()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test(self) -> Tuple[bool, str]:
|
||||||
|
"""
|
||||||
|
测试模块连接性
|
||||||
|
"""
|
||||||
|
ret = RequestUtils().get_res("https://api.bgm.tv/")
|
||||||
|
if ret and ret.status_code == 200:
|
||||||
|
return True, ""
|
||||||
|
elif ret:
|
||||||
|
return False, f"无法连接Bangumi,错误码:{ret.status_code}"
|
||||||
|
return False, "Bangumi网络连接失败"
|
||||||
|
|
||||||
|
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def recognize_media(self, bangumiid: int = None,
|
||||||
|
**kwargs) -> Optional[MediaInfo]:
|
||||||
|
"""
|
||||||
|
识别媒体信息
|
||||||
|
:param bangumiid: 识别的Bangumi ID
|
||||||
|
:return: 识别的媒体信息,包括剧集信息
|
||||||
|
"""
|
||||||
|
if not bangumiid:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 直接查询详情
|
||||||
|
info = self.bangumi_info(bangumiid=bangumiid)
|
||||||
|
if info:
|
||||||
|
# 赋值TMDB信息并返回
|
||||||
|
mediainfo = MediaInfo(bangumi_info=info)
|
||||||
|
logger.info(f"{bangumiid} Bangumi识别结果:{mediainfo.type.value} "
|
||||||
|
f"{mediainfo.title_year}")
|
||||||
|
return mediainfo
|
||||||
|
else:
|
||||||
|
logger.info(f"{bangumiid} 未匹配到Bangumi媒体信息")
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def bangumi_info(self, bangumiid: int) -> Optional[dict]:
|
||||||
|
"""
|
||||||
|
获取Bangumi信息
|
||||||
|
:param bangumiid: BangumiID
|
||||||
|
:return: Bangumi信息
|
||||||
|
"""
|
||||||
|
if not bangumiid:
|
||||||
|
return None
|
||||||
|
logger.info(f"开始获取Bangumi信息:{bangumiid} ...")
|
||||||
|
return self.bangumiapi.detail(bangumiid)
|
||||||
|
|
||||||
|
def bangumi_calendar(self, page: int = 1, count: int = 30) -> Optional[List[dict]]:
|
||||||
|
"""
|
||||||
|
获取Bangumi每日放送
|
||||||
|
:param page: 页码
|
||||||
|
:param count: 每页数量
|
||||||
|
"""
|
||||||
|
return self.bangumiapi.calendar(page, count)
|
||||||
|
|
||||||
|
def bangumi_credits(self, bangumiid: int, page: int = 1, count: int = 20) -> List[dict]:
|
||||||
|
"""
|
||||||
|
根据TMDBID查询电影演职员表
|
||||||
|
:param bangumiid: BangumiID
|
||||||
|
:param page: 页码
|
||||||
|
:param count: 数量
|
||||||
|
"""
|
||||||
|
persons = self.bangumiapi.persons(bangumiid) or []
|
||||||
|
if persons:
|
||||||
|
return persons[(page - 1) * count: page * count]
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def bangumi_recommend(self, bangumiid: int) -> List[dict]:
|
||||||
|
"""
|
||||||
|
根据BangumiID查询推荐电影
|
||||||
|
:param bangumiid: BangumiID
|
||||||
|
"""
|
||||||
|
return self.bangumiapi.subjects(bangumiid) or []
|
154
app/modules/bangumi/bangumi.py
Normal file
154
app/modules/bangumi/bangumi.py
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
from functools import lru_cache
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from app.utils.http import RequestUtils
|
||||||
|
|
||||||
|
|
||||||
|
class BangumiApi(object):
|
||||||
|
"""
|
||||||
|
https://bangumi.github.io/api/
|
||||||
|
"""
|
||||||
|
|
||||||
|
_urls = {
|
||||||
|
"calendar": "calendar",
|
||||||
|
"detail": "v0/subjects/%s",
|
||||||
|
"persons": "v0/subjects/%s/persons",
|
||||||
|
"subjects": "v0/subjects/%s/subjects"
|
||||||
|
}
|
||||||
|
_base_url = "https://api.bgm.tv/"
|
||||||
|
_req = RequestUtils(session=requests.Session())
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@lru_cache(maxsize=128)
|
||||||
|
def __invoke(cls, url, **kwargs):
|
||||||
|
req_url = cls._base_url + url
|
||||||
|
params = {}
|
||||||
|
if kwargs:
|
||||||
|
params.update(kwargs)
|
||||||
|
resp = cls._req.get_res(url=req_url, params=params)
|
||||||
|
try:
|
||||||
|
return resp.json() if resp else None
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def calendar(self, page: int = 1, count: int = 30):
|
||||||
|
"""
|
||||||
|
获取每日放送,返回items
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"weekday": {
|
||||||
|
"en": "Mon",
|
||||||
|
"cn": "星期一",
|
||||||
|
"ja": "月耀日",
|
||||||
|
"id": 1
|
||||||
|
},
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": 350235,
|
||||||
|
"url": "http://bgm.tv/subject/350235",
|
||||||
|
"type": 2,
|
||||||
|
"name": "月が導く異世界道中 第二幕",
|
||||||
|
"name_cn": "月光下的异世界之旅 第二幕",
|
||||||
|
"summary": "",
|
||||||
|
"air_date": "2024-01-08",
|
||||||
|
"air_weekday": 1,
|
||||||
|
"rating": {
|
||||||
|
"total": 257,
|
||||||
|
"count": {
|
||||||
|
"1": 1,
|
||||||
|
"2": 1,
|
||||||
|
"3": 4,
|
||||||
|
"4": 15,
|
||||||
|
"5": 51,
|
||||||
|
"6": 111,
|
||||||
|
"7": 49,
|
||||||
|
"8": 13,
|
||||||
|
"9": 5,
|
||||||
|
"10": 7
|
||||||
|
},
|
||||||
|
"score": 6.1
|
||||||
|
},
|
||||||
|
"rank": 6125,
|
||||||
|
"images": {
|
||||||
|
"large": "http://lain.bgm.tv/pic/cover/l/3c/a5/350235_A0USf.jpg",
|
||||||
|
"common": "http://lain.bgm.tv/pic/cover/c/3c/a5/350235_A0USf.jpg",
|
||||||
|
"medium": "http://lain.bgm.tv/pic/cover/m/3c/a5/350235_A0USf.jpg",
|
||||||
|
"small": "http://lain.bgm.tv/pic/cover/s/3c/a5/350235_A0USf.jpg",
|
||||||
|
"grid": "http://lain.bgm.tv/pic/cover/g/3c/a5/350235_A0USf.jpg"
|
||||||
|
},
|
||||||
|
"collection": {
|
||||||
|
"doing": 920
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 358561,
|
||||||
|
"url": "http://bgm.tv/subject/358561",
|
||||||
|
"type": 2,
|
||||||
|
"name": "大宇宙时代",
|
||||||
|
"name_cn": "大宇宙时代",
|
||||||
|
"summary": "",
|
||||||
|
"air_date": "2024-01-22",
|
||||||
|
"air_weekday": 1,
|
||||||
|
"rating": {
|
||||||
|
"total": 2,
|
||||||
|
"count": {
|
||||||
|
"1": 0,
|
||||||
|
"2": 0,
|
||||||
|
"3": 0,
|
||||||
|
"4": 0,
|
||||||
|
"5": 1,
|
||||||
|
"6": 1,
|
||||||
|
"7": 0,
|
||||||
|
"8": 0,
|
||||||
|
"9": 0,
|
||||||
|
"10": 0
|
||||||
|
},
|
||||||
|
"score": 5.5
|
||||||
|
},
|
||||||
|
"images": {
|
||||||
|
"large": "http://lain.bgm.tv/pic/cover/l/71/66/358561_UzsLu.jpg",
|
||||||
|
"common": "http://lain.bgm.tv/pic/cover/c/71/66/358561_UzsLu.jpg",
|
||||||
|
"medium": "http://lain.bgm.tv/pic/cover/m/71/66/358561_UzsLu.jpg",
|
||||||
|
"small": "http://lain.bgm.tv/pic/cover/s/71/66/358561_UzsLu.jpg",
|
||||||
|
"grid": "http://lain.bgm.tv/pic/cover/g/71/66/358561_UzsLu.jpg"
|
||||||
|
},
|
||||||
|
"collection": {
|
||||||
|
"doing": 9
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"""
|
||||||
|
ret_list = []
|
||||||
|
result = self.__invoke(self._urls["calendar"], _ts=datetime.strftime(datetime.now(), '%Y%m%d'))
|
||||||
|
if result:
|
||||||
|
for item in result:
|
||||||
|
ret_list.extend(item.get("items") or [])
|
||||||
|
return ret_list[(page - 1) * count: page * count]
|
||||||
|
|
||||||
|
def detail(self, bid: int):
|
||||||
|
"""
|
||||||
|
获取番剧详情
|
||||||
|
"""
|
||||||
|
return self.__invoke(self._urls["detail"] % bid, _ts=datetime.strftime(datetime.now(), '%Y%m%d'))
|
||||||
|
|
||||||
|
def persons(self, bid: int):
|
||||||
|
"""
|
||||||
|
获取番剧人物
|
||||||
|
"""
|
||||||
|
return self.__invoke(self._urls["persons"] % bid, _ts=datetime.strftime(datetime.now(), '%Y%m%d'))
|
||||||
|
|
||||||
|
def subjects(self, bid: int):
|
||||||
|
"""
|
||||||
|
获取关联条目信息
|
||||||
|
"""
|
||||||
|
return self.__invoke(self._urls["subjects"] % bid, _ts=datetime.strftime(datetime.now(), '%Y%m%d'))
|
@ -14,3 +14,5 @@ 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 *
|
||||||
|
12
app/schemas/bangumi.py
Normal file
12
app/schemas/bangumi.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
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
|
@ -83,6 +83,8 @@ class MediaInfo(BaseModel):
|
|||||||
tvdb_id: Optional[str] = None
|
tvdb_id: Optional[str] = None
|
||||||
# 豆瓣ID
|
# 豆瓣ID
|
||||||
douban_id: Optional[str] = None
|
douban_id: Optional[str] = None
|
||||||
|
# Bangumi ID
|
||||||
|
bangumi_id: Optional[int] = None
|
||||||
# 媒体原语种
|
# 媒体原语种
|
||||||
original_language: Optional[str] = None
|
original_language: Optional[str] = None
|
||||||
# 媒体原发行标题
|
# 媒体原发行标题
|
||||||
|
14
app/schemas/douban.py
Normal file
14
app/schemas/douban.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
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
|
@ -15,6 +15,7 @@ class Subscribe(BaseModel):
|
|||||||
keyword: Optional[str] = None
|
keyword: Optional[str] = None
|
||||||
tmdbid: Optional[int] = None
|
tmdbid: Optional[int] = None
|
||||||
doubanid: Optional[str] = None
|
doubanid: Optional[str] = None
|
||||||
|
bangumiid: Optional[int] = None
|
||||||
# 季号
|
# 季号
|
||||||
season: Optional[int] = None
|
season: Optional[int] = None
|
||||||
# 海报
|
# 海报
|
||||||
|
@ -49,14 +49,3 @@ class TmdbPerson(BaseModel):
|
|||||||
popularity: Optional[float] = None
|
popularity: Optional[float] = None
|
||||||
images: Optional[dict] = {}
|
images: Optional[dict] = {}
|
||||||
biography: Optional[str] = None
|
biography: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
|
34
database/versions/d146dea51516_1_0_16.py
Normal file
34
database/versions/d146dea51516_1_0_16.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
"""1.0.16
|
||||||
|
|
||||||
|
Revision ID: d146dea51516
|
||||||
|
Revises: 5813aaa7cb3a
|
||||||
|
Create Date: 2024-03-18 18:13:38.099531
|
||||||
|
|
||||||
|
"""
|
||||||
|
import contextlib
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'd146dea51516'
|
||||||
|
down_revision = '5813aaa7cb3a'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with contextlib.suppress(Exception):
|
||||||
|
with op.batch_alter_table("subscribe") as batch_op:
|
||||||
|
batch_op.add_column(sa.Column('bangumiid', sa.Integer, nullable=True))
|
||||||
|
try:
|
||||||
|
op.create_index('ix_subscribe_bangumiid', 'subscribe', ['bangumiid'], unique=False)
|
||||||
|
except Exception as err:
|
||||||
|
pass
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
pass
|
Loading…
x
Reference in New Issue
Block a user