fix api
This commit is contained in:
parent
f85e960fa9
commit
acdec220f7
@ -1,6 +1,6 @@
|
|||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
|
||||||
from app.api.endpoints import login, user, site, message, webhook, subscribe, media, douban
|
from app.api.endpoints import login, user, site, message, webhook, subscribe, media, douban, search
|
||||||
|
|
||||||
api_router = APIRouter()
|
api_router = APIRouter()
|
||||||
api_router.include_router(login.router, tags=["login"])
|
api_router.include_router(login.router, tags=["login"])
|
||||||
@ -10,4 +10,5 @@ api_router.include_router(message.router, prefix="/message", tags=["message"])
|
|||||||
api_router.include_router(webhook.router, prefix="/webhook", tags=["webhook"])
|
api_router.include_router(webhook.router, prefix="/webhook", tags=["webhook"])
|
||||||
api_router.include_router(subscribe.router, prefix="/subscribe", tags=["subscribe"])
|
api_router.include_router(subscribe.router, prefix="/subscribe", tags=["subscribe"])
|
||||||
api_router.include_router(media.router, prefix="/media", tags=["media"])
|
api_router.include_router(media.router, prefix="/media", tags=["media"])
|
||||||
|
api_router.include_router(search.router, prefix="/search", tags=["search"])
|
||||||
api_router.include_router(douban.router, prefix="/douban", tags=["douban"])
|
api_router.include_router(douban.router, prefix="/douban", tags=["douban"])
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
|
from typing import Any
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, BackgroundTasks
|
from fastapi import APIRouter, Depends, BackgroundTasks
|
||||||
|
|
||||||
from app import schemas
|
from app import schemas
|
||||||
from app.chain.douban_sync import DoubanSyncChain
|
from app.chain.douban import DoubanChain
|
||||||
from app.db.models.user import User
|
from app.db.models.user import User
|
||||||
from app.db.userauth import get_current_active_superuser
|
from app.db.userauth import get_current_active_superuser
|
||||||
|
|
||||||
@ -12,15 +14,15 @@ def start_douban_chain():
|
|||||||
"""
|
"""
|
||||||
启动链式任务
|
启动链式任务
|
||||||
"""
|
"""
|
||||||
DoubanSyncChain().process()
|
DoubanChain().sync()
|
||||||
|
|
||||||
|
|
||||||
@router.get("/sync", response_model=schemas.Response)
|
@router.get("/sync", response_model=schemas.Response)
|
||||||
async def sync_douban(
|
async def sync_douban(
|
||||||
background_tasks: BackgroundTasks,
|
background_tasks: BackgroundTasks,
|
||||||
_: User = Depends(get_current_active_superuser)):
|
_: User = Depends(get_current_active_superuser)) -> Any:
|
||||||
"""
|
"""
|
||||||
查询所有订阅
|
同步豆瓣想看
|
||||||
"""
|
"""
|
||||||
background_tasks.add_task(start_douban_chain)
|
background_tasks.add_task(start_douban_chain)
|
||||||
return {"success": True}
|
return {"success": True}
|
||||||
|
@ -1,20 +1,59 @@
|
|||||||
|
from typing import List, Any
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Depends
|
||||||
|
|
||||||
from app import schemas
|
from app import schemas
|
||||||
from app.chain.identify import IdentifyChain
|
from app.chain.media import MediaChain
|
||||||
|
from app.core.context import MediaInfo
|
||||||
from app.db.models.user import User
|
from app.db.models.user import User
|
||||||
from app.db.userauth import get_current_active_user
|
from app.db.userauth import get_current_active_user
|
||||||
|
from app.schemas.types import MediaType
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@router.post("/recognize", response_model=schemas.Context)
|
@router.get("/recognize", response_model=schemas.Context)
|
||||||
async def recognize(title: str,
|
async def recognize(title: str,
|
||||||
subtitle: str = None,
|
subtitle: str = None,
|
||||||
_: User = Depends(get_current_active_user)):
|
_: User = Depends(get_current_active_user)) -> Any:
|
||||||
"""
|
"""
|
||||||
识别媒体信息
|
识别媒体信息
|
||||||
"""
|
"""
|
||||||
# 识别媒体信息
|
# 识别媒体信息
|
||||||
context = IdentifyChain().process(title=title, subtitle=subtitle)
|
context = MediaChain().recognize_by_title(title=title, subtitle=subtitle)
|
||||||
return context.to_dict()
|
return context.to_dict()
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/tmdb", response_model=schemas.MediaInfo)
|
||||||
|
async def tmdb_info(tmdbid: int, type_name: str) -> Any:
|
||||||
|
"""
|
||||||
|
根据TMDBID查询媒体信息
|
||||||
|
"""
|
||||||
|
mtype = MediaType.MOVIE if type_name == MediaType.MOVIE.value else MediaType.TV
|
||||||
|
media = MediaChain().recognize_media(tmdbid=tmdbid, mtype=mtype)
|
||||||
|
if media:
|
||||||
|
return media.to_dict()
|
||||||
|
else:
|
||||||
|
return schemas.MediaInfo()
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/douban", response_model=schemas.MediaInfo)
|
||||||
|
async def douban_info(doubanid: str) -> Any:
|
||||||
|
"""
|
||||||
|
根据豆瓣ID查询豆瓣媒体信息
|
||||||
|
"""
|
||||||
|
doubaninfo = MediaChain().douban_info(doubanid=doubanid)
|
||||||
|
if doubaninfo:
|
||||||
|
return MediaInfo(douban_info=doubaninfo).to_dict()
|
||||||
|
else:
|
||||||
|
return schemas.MediaInfo()
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/search", response_model=List[schemas.MediaInfo])
|
||||||
|
async def search_by_title(title: str,
|
||||||
|
_: User = Depends(get_current_active_user)) -> Any:
|
||||||
|
"""
|
||||||
|
搜索媒体信息
|
||||||
|
"""
|
||||||
|
_, medias = MediaChain().search(title=title)
|
||||||
|
return [media.to_dict() for media in medias]
|
||||||
|
@ -5,7 +5,7 @@ from fastapi import Request
|
|||||||
from starlette.responses import PlainTextResponse
|
from starlette.responses import PlainTextResponse
|
||||||
|
|
||||||
from app import schemas
|
from app import schemas
|
||||||
from app.chain.user_message import UserMessageChain
|
from app.chain.message import MessageChain
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.modules.wechat.WXBizMsgCrypt3 import WXBizMsgCrypt
|
from app.modules.wechat.WXBizMsgCrypt3 import WXBizMsgCrypt
|
||||||
@ -17,7 +17,7 @@ def start_message_chain(body: Any, form: Any, args: Any):
|
|||||||
"""
|
"""
|
||||||
启动链式任务
|
启动链式任务
|
||||||
"""
|
"""
|
||||||
UserMessageChain().process(body=body, form=form, args=args)
|
MessageChain().process(body=body, form=form, args=args)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/", response_model=schemas.Response)
|
@router.post("/", response_model=schemas.Response)
|
||||||
@ -33,7 +33,8 @@ async def user_message(background_tasks: BackgroundTasks, request: Request):
|
|||||||
|
|
||||||
|
|
||||||
@router.get("/")
|
@router.get("/")
|
||||||
async def wechat_verify(echostr: str, msg_signature: str, timestamp: Union[str, int], nonce: str):
|
async def wechat_verify(echostr: str, msg_signature: str,
|
||||||
|
timestamp: Union[str, int], nonce: str) -> Any:
|
||||||
"""
|
"""
|
||||||
用户消息响应
|
用户消息响应
|
||||||
"""
|
"""
|
||||||
|
24
app/api/endpoints/search.py
Normal file
24
app/api/endpoints/search.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
from typing import List, Any
|
||||||
|
|
||||||
|
from fastapi import APIRouter, Depends
|
||||||
|
|
||||||
|
from app import schemas
|
||||||
|
from app.chain.search import SearchChain
|
||||||
|
from app.db.models.user import User
|
||||||
|
from app.db.userauth import get_current_active_user
|
||||||
|
from app.schemas.types import MediaType
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/tmdbid", response_model=List[schemas.Context])
|
||||||
|
async def search_by_tmdbid(tmdbid: int,
|
||||||
|
mtype: str = None,
|
||||||
|
_: User = Depends(get_current_active_user)) -> Any:
|
||||||
|
"""
|
||||||
|
根据TMDBID搜索资源
|
||||||
|
"""
|
||||||
|
if mtype:
|
||||||
|
mtype = MediaType.TV if mtype == MediaType.TV.value else MediaType.MOVIE
|
||||||
|
torrents = SearchChain().search_by_tmdbid(tmdbid=tmdbid, mtype=mtype)
|
||||||
|
return [torrent.to_dict() for torrent in torrents]
|
@ -43,7 +43,7 @@ async def update_site(
|
|||||||
|
|
||||||
|
|
||||||
@router.get("/cookiecloud", response_model=schemas.Response)
|
@router.get("/cookiecloud", response_model=schemas.Response)
|
||||||
async def cookie_cloud_sync(_: User = Depends(get_current_active_user)) -> dict:
|
async def cookie_cloud_sync(_: User = Depends(get_current_active_user)) -> Any:
|
||||||
"""
|
"""
|
||||||
运行CookieCloud同步站点信息
|
运行CookieCloud同步站点信息
|
||||||
"""
|
"""
|
||||||
|
@ -20,14 +20,14 @@ def start_subscribe_chain(title: str, year: str,
|
|||||||
"""
|
"""
|
||||||
启动订阅链式任务
|
启动订阅链式任务
|
||||||
"""
|
"""
|
||||||
SubscribeChain().process(title=title, year=year,
|
SubscribeChain().add(title=title, year=year,
|
||||||
mtype=mtype, tmdbid=tmdbid, season=season, username=username)
|
mtype=mtype, tmdbid=tmdbid, season=season, username=username)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/", response_model=List[schemas.Subscribe])
|
@router.get("/", response_model=List[schemas.Subscribe])
|
||||||
async def read_subscribes(
|
async def read_subscribes(
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
_: User = Depends(get_current_active_superuser)):
|
_: User = Depends(get_current_active_superuser)) -> Any:
|
||||||
"""
|
"""
|
||||||
查询所有订阅
|
查询所有订阅
|
||||||
"""
|
"""
|
||||||
@ -43,7 +43,7 @@ async def create_subscribe(
|
|||||||
"""
|
"""
|
||||||
新增订阅
|
新增订阅
|
||||||
"""
|
"""
|
||||||
result = SubscribeChain().process(**subscribe_in.dict())
|
result = SubscribeChain().add(**subscribe_in.dict())
|
||||||
return {"success": result}
|
return {"success": result}
|
||||||
|
|
||||||
|
|
||||||
@ -83,7 +83,7 @@ async def delete_subscribe(
|
|||||||
|
|
||||||
@router.post("/seerr", response_model=schemas.Response)
|
@router.post("/seerr", response_model=schemas.Response)
|
||||||
async def seerr_subscribe(request: Request, background_tasks: BackgroundTasks,
|
async def seerr_subscribe(request: Request, background_tasks: BackgroundTasks,
|
||||||
authorization: str = Header(None)):
|
authorization: str = Header(None)) -> Any:
|
||||||
"""
|
"""
|
||||||
Jellyseerr/Overseerr订阅
|
Jellyseerr/Overseerr订阅
|
||||||
"""
|
"""
|
||||||
@ -136,7 +136,7 @@ async def seerr_subscribe(request: Request, background_tasks: BackgroundTasks,
|
|||||||
|
|
||||||
@router.get("/refresh", response_model=schemas.Response)
|
@router.get("/refresh", response_model=schemas.Response)
|
||||||
async def refresh_subscribes(
|
async def refresh_subscribes(
|
||||||
_: User = Depends(get_current_active_superuser)):
|
_: User = Depends(get_current_active_superuser)) -> Any:
|
||||||
"""
|
"""
|
||||||
刷新所有订阅
|
刷新所有订阅
|
||||||
"""
|
"""
|
||||||
@ -146,7 +146,7 @@ async def refresh_subscribes(
|
|||||||
|
|
||||||
@router.get("/search", response_model=schemas.Response)
|
@router.get("/search", response_model=schemas.Response)
|
||||||
async def search_subscribes(
|
async def search_subscribes(
|
||||||
_: User = Depends(get_current_active_superuser)):
|
_: User = Depends(get_current_active_superuser)) -> Any:
|
||||||
"""
|
"""
|
||||||
搜索所有订阅
|
搜索所有订阅
|
||||||
"""
|
"""
|
||||||
|
@ -3,7 +3,7 @@ from typing import Any
|
|||||||
from fastapi import APIRouter, BackgroundTasks, Request
|
from fastapi import APIRouter, BackgroundTasks, Request
|
||||||
|
|
||||||
from app import schemas
|
from app import schemas
|
||||||
from app.chain.webhook_message import WebhookMessageChain
|
from app.chain.webhook import WebhookChain
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
@ -13,11 +13,12 @@ def start_webhook_chain(body: Any, form: Any, args: Any):
|
|||||||
"""
|
"""
|
||||||
启动链式任务
|
启动链式任务
|
||||||
"""
|
"""
|
||||||
WebhookMessageChain().process(body=body, form=form, args=args)
|
WebhookChain().message(body=body, form=form, args=args)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/", response_model=schemas.Response)
|
@router.post("/", response_model=schemas.Response)
|
||||||
async def webhook_message(background_tasks: BackgroundTasks, token: str, request: Request):
|
async def webhook_message(background_tasks: BackgroundTasks,
|
||||||
|
token: str, request: Request) -> Any:
|
||||||
"""
|
"""
|
||||||
Webhook响应
|
Webhook响应
|
||||||
"""
|
"""
|
||||||
|
@ -4,7 +4,7 @@ from fastapi import APIRouter, HTTPException, Depends
|
|||||||
from requests import Session
|
from requests import Session
|
||||||
|
|
||||||
from app import schemas
|
from app import schemas
|
||||||
from app.chain.identify import IdentifyChain
|
from app.chain.media import MediaChain
|
||||||
from app.chain.subscribe import SubscribeChain
|
from app.chain.subscribe import SubscribeChain
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
from app.core.metainfo import MetaInfo
|
from app.core.metainfo import MetaInfo
|
||||||
@ -232,11 +232,11 @@ async def arr_movie_lookup(apikey: str, term: str, db: Session = Depends(get_db)
|
|||||||
)
|
)
|
||||||
tmdbid = term.replace("tmdb:", "")
|
tmdbid = term.replace("tmdb:", "")
|
||||||
# 查询媒体信息
|
# 查询媒体信息
|
||||||
mediainfo = IdentifyChain().recognize_media(mtype=MediaType.MOVIE, tmdbid=int(tmdbid))
|
mediainfo = MediaChain().recognize_media(mtype=MediaType.MOVIE, tmdbid=int(tmdbid))
|
||||||
if not mediainfo:
|
if not mediainfo:
|
||||||
return [RadarrMovie()]
|
return [RadarrMovie()]
|
||||||
# 查询是否已存在
|
# 查询是否已存在
|
||||||
exists = IdentifyChain().media_exists(mediainfo=mediainfo)
|
exists = MediaChain().media_exists(mediainfo=mediainfo)
|
||||||
if not exists:
|
if not exists:
|
||||||
# 文件不存在
|
# 文件不存在
|
||||||
hasfile = False
|
hasfile = False
|
||||||
@ -311,7 +311,7 @@ async def arr_add_movie(apikey: str, movie: RadarrMovie) -> Any:
|
|||||||
status_code=403,
|
status_code=403,
|
||||||
detail="认证失败!",
|
detail="认证失败!",
|
||||||
)
|
)
|
||||||
sid = SubscribeChain().process(title=movie.title,
|
sid = SubscribeChain().add(title=movie.title,
|
||||||
year=movie.year,
|
year=movie.year,
|
||||||
mtype=MediaType.MOVIE,
|
mtype=MediaType.MOVIE,
|
||||||
tmdbid=movie.tmdbId,
|
tmdbid=movie.tmdbId,
|
||||||
@ -501,7 +501,7 @@ async def arr_series_lookup(apikey: str, term: str, db: Session = Depends(get_db
|
|||||||
)
|
)
|
||||||
# 查询TMDB媒体信息
|
# 查询TMDB媒体信息
|
||||||
if not term.startswith("tvdb:"):
|
if not term.startswith("tvdb:"):
|
||||||
mediainfo = IdentifyChain().recognize_media(meta=MetaInfo(term),
|
mediainfo = MediaChain().recognize_media(meta=MetaInfo(term),
|
||||||
mtype=MediaType.MOVIE)
|
mtype=MediaType.MOVIE)
|
||||||
if not mediainfo:
|
if not mediainfo:
|
||||||
return [SonarrSeries()]
|
return [SonarrSeries()]
|
||||||
@ -509,7 +509,7 @@ async def arr_series_lookup(apikey: str, term: str, db: Session = Depends(get_db
|
|||||||
tmdbid = mediainfo.tmdb_id
|
tmdbid = mediainfo.tmdb_id
|
||||||
else:
|
else:
|
||||||
tvdbid = int(term.replace("tvdb:", ""))
|
tvdbid = int(term.replace("tvdb:", ""))
|
||||||
mediainfo = IdentifyChain().recognize_media(mtype=MediaType.MOVIE,
|
mediainfo = MediaChain().recognize_media(mtype=MediaType.MOVIE,
|
||||||
tmdbid=tvdbid)
|
tmdbid=tvdbid)
|
||||||
if not mediainfo:
|
if not mediainfo:
|
||||||
return [SonarrSeries()]
|
return [SonarrSeries()]
|
||||||
@ -517,13 +517,13 @@ async def arr_series_lookup(apikey: str, term: str, db: Session = Depends(get_db
|
|||||||
# 查询TVDB季信息
|
# 查询TVDB季信息
|
||||||
seas: List[int] = []
|
seas: List[int] = []
|
||||||
if tvdbid:
|
if tvdbid:
|
||||||
tvdbinfo = IdentifyChain().tvdb_info(tvdbid=tvdbid)
|
tvdbinfo = MediaChain().tvdb_info(tvdbid=tvdbid)
|
||||||
if tvdbinfo:
|
if tvdbinfo:
|
||||||
sea_num = tvdbinfo.get('season')
|
sea_num = tvdbinfo.get('season')
|
||||||
if sea_num:
|
if sea_num:
|
||||||
seas = list(range(1, int(sea_num) + 1))
|
seas = list(range(1, int(sea_num) + 1))
|
||||||
# 查询是否存在
|
# 查询是否存在
|
||||||
exists = IdentifyChain().media_exists(mediainfo)
|
exists = MediaChain().media_exists(mediainfo)
|
||||||
if exists:
|
if exists:
|
||||||
hasfile = True
|
hasfile = True
|
||||||
else:
|
else:
|
||||||
@ -629,7 +629,7 @@ async def arr_add_series(apikey: str, tv: schemas.SonarrSeries) -> Any:
|
|||||||
for season in tv.seasons:
|
for season in tv.seasons:
|
||||||
if not season.get("monitored"):
|
if not season.get("monitored"):
|
||||||
continue
|
continue
|
||||||
sid = SubscribeChain().process(title=tv.title,
|
sid = SubscribeChain().add(title=tv.title,
|
||||||
year=tv.year,
|
year=tv.year,
|
||||||
season=season.get("seasonNumber"),
|
season=season.get("seasonNumber"),
|
||||||
tmdbid=tv.tmdbId,
|
tmdbid=tv.tmdbId,
|
||||||
|
@ -1,19 +1,18 @@
|
|||||||
import traceback
|
import traceback
|
||||||
from abc import abstractmethod
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional, Any, Tuple, List, Set, Union, Dict
|
from typing import Optional, Any, Tuple, List, Set, Union, Dict
|
||||||
|
|
||||||
from ruamel.yaml import CommentedMap
|
from ruamel.yaml import CommentedMap
|
||||||
|
|
||||||
from app.core.context import Context
|
from app.core.context import Context
|
||||||
from app.core.event import EventManager
|
|
||||||
from app.core.module import ModuleManager
|
|
||||||
from app.core.context import MediaInfo, TorrentInfo
|
from app.core.context import MediaInfo, TorrentInfo
|
||||||
|
from app.core.event import EventManager
|
||||||
from app.core.meta import MetaBase
|
from app.core.meta import MetaBase
|
||||||
|
from app.core.module import ModuleManager
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.schemas.context import TransferInfo, TransferTorrent, ExistMediaInfo, DownloadingTorrent
|
from app.schemas import TransferInfo, TransferTorrent, ExistMediaInfo, DownloadingTorrent
|
||||||
from app.utils.singleton import AbstractSingleton, Singleton
|
|
||||||
from app.schemas.types import TorrentStatus, MediaType
|
from app.schemas.types import TorrentStatus, MediaType
|
||||||
|
from app.utils.singleton import AbstractSingleton, Singleton
|
||||||
|
|
||||||
|
|
||||||
class ChainBase(AbstractSingleton, metaclass=Singleton):
|
class ChainBase(AbstractSingleton, metaclass=Singleton):
|
||||||
@ -28,14 +27,7 @@ class ChainBase(AbstractSingleton, metaclass=Singleton):
|
|||||||
self.modulemanager = ModuleManager()
|
self.modulemanager = ModuleManager()
|
||||||
self.eventmanager = EventManager()
|
self.eventmanager = EventManager()
|
||||||
|
|
||||||
@abstractmethod
|
def __run_module(self, method: str, *args, **kwargs) -> Any:
|
||||||
def process(self, *args, **kwargs) -> Optional[Context]:
|
|
||||||
"""
|
|
||||||
处理链,返回上下文
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def run_module(self, method: str, *args, **kwargs) -> Any:
|
|
||||||
"""
|
"""
|
||||||
运行包含该方法的所有模块,然后返回结果
|
运行包含该方法的所有模块,然后返回结果
|
||||||
"""
|
"""
|
||||||
@ -69,84 +61,84 @@ class ChainBase(AbstractSingleton, metaclass=Singleton):
|
|||||||
|
|
||||||
def prepare_recognize(self, title: str,
|
def prepare_recognize(self, title: str,
|
||||||
subtitle: str = None) -> Tuple[str, str]:
|
subtitle: str = None) -> Tuple[str, str]:
|
||||||
return self.run_module("prepare_recognize", title=title, subtitle=subtitle)
|
return self.__run_module("prepare_recognize", title=title, subtitle=subtitle)
|
||||||
|
|
||||||
def recognize_media(self, meta: MetaBase = None,
|
def recognize_media(self, meta: MetaBase = None,
|
||||||
mtype: MediaType = None,
|
mtype: MediaType = None,
|
||||||
tmdbid: int = None) -> Optional[MediaInfo]:
|
tmdbid: int = None) -> Optional[MediaInfo]:
|
||||||
return self.run_module("recognize_media", meta=meta, mtype=mtype, tmdbid=tmdbid)
|
return self.__run_module("recognize_media", meta=meta, mtype=mtype, tmdbid=tmdbid)
|
||||||
|
|
||||||
def obtain_image(self, mediainfo: MediaInfo) -> Optional[MediaInfo]:
|
def obtain_image(self, mediainfo: MediaInfo) -> Optional[MediaInfo]:
|
||||||
return self.run_module("obtain_image", mediainfo=mediainfo)
|
return self.__run_module("obtain_image", mediainfo=mediainfo)
|
||||||
|
|
||||||
def douban_info(self, doubanid: str) -> Optional[dict]:
|
def douban_info(self, doubanid: str) -> Optional[dict]:
|
||||||
return self.run_module("douban_info", doubanid=doubanid)
|
return self.__run_module("douban_info", doubanid=doubanid)
|
||||||
|
|
||||||
def tvdb_info(self, tvdbid: int) -> Optional[dict]:
|
def tvdb_info(self, tvdbid: int) -> Optional[dict]:
|
||||||
return self.run_module("tvdb_info", tvdbid=tvdbid)
|
return self.__run_module("tvdb_info", tvdbid=tvdbid)
|
||||||
|
|
||||||
def message_parser(self, body: Any, form: Any, args: Any) -> Optional[dict]:
|
def message_parser(self, body: Any, form: Any, args: Any) -> Optional[dict]:
|
||||||
return self.run_module("message_parser", body=body, form=form, args=args)
|
return self.__run_module("message_parser", body=body, form=form, args=args)
|
||||||
|
|
||||||
def webhook_parser(self, body: Any, form: Any, args: Any) -> Optional[dict]:
|
def webhook_parser(self, body: Any, form: Any, args: Any) -> Optional[dict]:
|
||||||
return self.run_module("webhook_parser", body=body, form=form, args=args)
|
return self.__run_module("webhook_parser", body=body, form=form, args=args)
|
||||||
|
|
||||||
def search_medias(self, meta: MetaBase) -> Optional[List[MediaInfo]]:
|
def search_medias(self, meta: MetaBase) -> Optional[List[MediaInfo]]:
|
||||||
return self.run_module("search_medias", meta=meta)
|
return self.__run_module("search_medias", meta=meta)
|
||||||
|
|
||||||
def search_torrents(self, mediainfo: Optional[MediaInfo], sites: List[CommentedMap],
|
def search_torrents(self, mediainfo: Optional[MediaInfo], sites: List[CommentedMap],
|
||||||
keyword: str = None) -> Optional[List[TorrentInfo]]:
|
keyword: str = None) -> Optional[List[TorrentInfo]]:
|
||||||
return self.run_module("search_torrents", mediainfo=mediainfo, sites=sites, keyword=keyword)
|
return self.__run_module("search_torrents", mediainfo=mediainfo, sites=sites, keyword=keyword)
|
||||||
|
|
||||||
def refresh_torrents(self, sites: List[CommentedMap]) -> Optional[List[TorrentInfo]]:
|
def refresh_torrents(self, sites: List[CommentedMap]) -> Optional[List[TorrentInfo]]:
|
||||||
return self.run_module("refresh_torrents", sites=sites)
|
return self.__run_module("refresh_torrents", sites=sites)
|
||||||
|
|
||||||
def filter_torrents(self, torrent_list: List[TorrentInfo],
|
def filter_torrents(self, torrent_list: List[TorrentInfo],
|
||||||
season_episodes: Dict[int, list] = None) -> List[TorrentInfo]:
|
season_episodes: Dict[int, list] = None) -> List[TorrentInfo]:
|
||||||
return self.run_module("filter_torrents", torrent_list=torrent_list, season_episodes=season_episodes)
|
return self.__run_module("filter_torrents", torrent_list=torrent_list, season_episodes=season_episodes)
|
||||||
|
|
||||||
def download(self, torrent_path: Path, cookie: str,
|
def download(self, torrent_path: Path, cookie: str,
|
||||||
episodes: Set[int] = None) -> Optional[Tuple[Optional[str], str]]:
|
episodes: Set[int] = None) -> Optional[Tuple[Optional[str], str]]:
|
||||||
return self.run_module("download", torrent_path=torrent_path, cookie=cookie, episodes=episodes)
|
return self.__run_module("download", torrent_path=torrent_path, cookie=cookie, episodes=episodes)
|
||||||
|
|
||||||
def download_added(self, context: Context, torrent_path: Path) -> None:
|
def download_added(self, context: Context, torrent_path: Path) -> None:
|
||||||
return self.run_module("download_added", context=context, torrent_path=torrent_path)
|
return self.__run_module("download_added", context=context, torrent_path=torrent_path)
|
||||||
|
|
||||||
def list_torrents(self, status: TorrentStatus = None,
|
def list_torrents(self, status: TorrentStatus = None,
|
||||||
hashs: Union[list, str] = None) -> Optional[List[Union[TransferTorrent, DownloadingTorrent]]]:
|
hashs: Union[list, str] = None) -> Optional[List[Union[TransferTorrent, DownloadingTorrent]]]:
|
||||||
return self.run_module("list_torrents", status=status, hashs=hashs)
|
return self.__run_module("list_torrents", status=status, hashs=hashs)
|
||||||
|
|
||||||
def transfer(self, path: Path, mediainfo: MediaInfo) -> Optional[TransferInfo]:
|
def transfer(self, path: Path, mediainfo: MediaInfo) -> Optional[TransferInfo]:
|
||||||
return self.run_module("transfer", path=path, mediainfo=mediainfo)
|
return self.__run_module("transfer", path=path, mediainfo=mediainfo)
|
||||||
|
|
||||||
def transfer_completed(self, hashs: Union[str, list], transinfo: TransferInfo) -> None:
|
def transfer_completed(self, hashs: Union[str, list], transinfo: TransferInfo) -> None:
|
||||||
return self.run_module("transfer_completed", hashs=hashs, transinfo=transinfo)
|
return self.__run_module("transfer_completed", hashs=hashs, transinfo=transinfo)
|
||||||
|
|
||||||
def remove_torrents(self, hashs: Union[str, list]) -> bool:
|
def remove_torrents(self, hashs: Union[str, list]) -> bool:
|
||||||
return self.run_module("remove_torrents", hashs=hashs)
|
return self.__run_module("remove_torrents", hashs=hashs)
|
||||||
|
|
||||||
def media_exists(self, mediainfo: MediaInfo) -> Optional[ExistMediaInfo]:
|
def media_exists(self, mediainfo: MediaInfo) -> Optional[ExistMediaInfo]:
|
||||||
return self.run_module("media_exists", mediainfo=mediainfo)
|
return self.__run_module("media_exists", mediainfo=mediainfo)
|
||||||
|
|
||||||
def refresh_mediaserver(self, mediainfo: MediaInfo, file_path: Path) -> Optional[bool]:
|
def refresh_mediaserver(self, mediainfo: MediaInfo, file_path: Path) -> Optional[bool]:
|
||||||
return self.run_module("refresh_mediaserver", mediainfo=mediainfo, file_path=file_path)
|
return self.__run_module("refresh_mediaserver", mediainfo=mediainfo, file_path=file_path)
|
||||||
|
|
||||||
def post_message(self, title: str, text: str = None,
|
def post_message(self, title: str, text: str = None,
|
||||||
image: str = None, userid: Union[str, int] = None) -> Optional[bool]:
|
image: str = None, userid: Union[str, int] = None) -> Optional[bool]:
|
||||||
return self.run_module("post_message", title=title, text=text, image=image, userid=userid)
|
return self.__run_module("post_message", title=title, text=text, image=image, userid=userid)
|
||||||
|
|
||||||
def post_medias_message(self, title: str, items: List[MediaInfo],
|
def post_medias_message(self, title: str, items: List[MediaInfo],
|
||||||
userid: Union[str, int] = None) -> Optional[bool]:
|
userid: Union[str, int] = None) -> Optional[bool]:
|
||||||
return self.run_module("post_medias_message", title=title, items=items, userid=userid)
|
return self.__run_module("post_medias_message", title=title, items=items, userid=userid)
|
||||||
|
|
||||||
def post_torrents_message(self, title: str, items: List[Context],
|
def post_torrents_message(self, title: str, items: List[Context],
|
||||||
mediainfo: MediaInfo,
|
mediainfo: MediaInfo,
|
||||||
userid: Union[str, int] = None) -> Optional[bool]:
|
userid: Union[str, int] = None) -> Optional[bool]:
|
||||||
return self.run_module("post_torrents_message", title=title, mediainfo=mediainfo,
|
return self.__run_module("post_torrents_message", title=title, mediainfo=mediainfo,
|
||||||
items=items, userid=userid)
|
items=items, userid=userid)
|
||||||
|
|
||||||
def scrape_metadata(self, path: Path, mediainfo: MediaInfo) -> None:
|
def scrape_metadata(self, path: Path, mediainfo: MediaInfo) -> None:
|
||||||
return self.run_module("scrape_metadata", path=path, mediainfo=mediainfo)
|
return self.__run_module("scrape_metadata", path=path, mediainfo=mediainfo)
|
||||||
|
|
||||||
def register_commands(self, commands: dict) -> None:
|
def register_commands(self, commands: dict) -> None:
|
||||||
return self.run_module("register_commands", commands=commands)
|
return self.__run_module("register_commands", commands=commands)
|
||||||
|
@ -12,7 +12,7 @@ from app.helper.rss import RssHelper
|
|||||||
from app.log import logger
|
from app.log import logger
|
||||||
|
|
||||||
|
|
||||||
class DoubanSyncChain(ChainBase):
|
class DoubanChain(ChainBase):
|
||||||
"""
|
"""
|
||||||
同步豆瓣想看数据
|
同步豆瓣想看数据
|
||||||
"""
|
"""
|
||||||
@ -28,7 +28,7 @@ class DoubanSyncChain(ChainBase):
|
|||||||
self.searchchain = SearchChain()
|
self.searchchain = SearchChain()
|
||||||
self.subscribechain = SubscribeChain()
|
self.subscribechain = SubscribeChain()
|
||||||
|
|
||||||
def process(self):
|
def sync(self):
|
||||||
"""
|
"""
|
||||||
通过用户RSS同步豆瓣想看数据
|
通过用户RSS同步豆瓣想看数据
|
||||||
"""
|
"""
|
||||||
@ -80,8 +80,7 @@ class DoubanSyncChain(ChainBase):
|
|||||||
continue
|
continue
|
||||||
logger.info(f'{mediainfo.title_year} 媒体库中不存在,开始搜索 ...')
|
logger.info(f'{mediainfo.title_year} 媒体库中不存在,开始搜索 ...')
|
||||||
# 搜索
|
# 搜索
|
||||||
contexts = self.searchchain.process(meta=meta,
|
contexts = self.searchchain.process(mediainfo=mediainfo,
|
||||||
mediainfo=mediainfo,
|
|
||||||
no_exists=no_exists)
|
no_exists=no_exists)
|
||||||
if not contexts:
|
if not contexts:
|
||||||
logger.warn(f'{mediainfo.title_year} 未搜索到资源')
|
logger.warn(f'{mediainfo.title_year} 未搜索到资源')
|
||||||
@ -95,7 +94,7 @@ class DoubanSyncChain(ChainBase):
|
|||||||
# 未完成下载
|
# 未完成下载
|
||||||
logger.info(f'{mediainfo.title_year} 未下载未完整,添加订阅 ...')
|
logger.info(f'{mediainfo.title_year} 未下载未完整,添加订阅 ...')
|
||||||
# 添加订阅
|
# 添加订阅
|
||||||
self.subscribechain.process(title=mediainfo.title,
|
self.subscribechain.add(title=mediainfo.title,
|
||||||
year=mediainfo.year,
|
year=mediainfo.year,
|
||||||
mtype=mediainfo.type,
|
mtype=mediainfo.type,
|
||||||
tmdbid=mediainfo.tmdb_id,
|
tmdbid=mediainfo.tmdb_id,
|
@ -8,7 +8,7 @@ from app.core.meta import MetaBase
|
|||||||
from app.db.downloadhistory_oper import DownloadHistoryOper
|
from app.db.downloadhistory_oper import DownloadHistoryOper
|
||||||
from app.helper.torrent import TorrentHelper
|
from app.helper.torrent import TorrentHelper
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.schemas.context import ExistMediaInfo, NotExistMediaInfo
|
from app.schemas import ExistMediaInfo, NotExistMediaInfo
|
||||||
from app.schemas.types import MediaType, TorrentStatus, EventType
|
from app.schemas.types import MediaType, TorrentStatus, EventType
|
||||||
from app.utils.string import StringUtils
|
from app.utils.string import StringUtils
|
||||||
|
|
||||||
@ -20,9 +20,6 @@ class DownloadChain(ChainBase):
|
|||||||
self.torrent = TorrentHelper()
|
self.torrent = TorrentHelper()
|
||||||
self.downloadhis = DownloadHistoryOper()
|
self.downloadhis = DownloadHistoryOper()
|
||||||
|
|
||||||
def process(self, *args, **kwargs) -> Optional[Context]:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def post_download_message(self, meta: MetaBase, mediainfo: MediaInfo, torrent: TorrentInfo, userid: str = None):
|
def post_download_message(self, meta: MetaBase, mediainfo: MediaInfo, torrent: TorrentInfo, userid: str = None):
|
||||||
"""
|
"""
|
||||||
发送添加下载的消息
|
发送添加下载的消息
|
||||||
|
@ -1,34 +0,0 @@
|
|||||||
from typing import Optional
|
|
||||||
|
|
||||||
from app.chain import ChainBase
|
|
||||||
from app.core.metainfo import MetaInfo
|
|
||||||
from app.core.context import Context, MediaInfo
|
|
||||||
from app.log import logger
|
|
||||||
|
|
||||||
|
|
||||||
class IdentifyChain(ChainBase):
|
|
||||||
"""
|
|
||||||
识别处理链
|
|
||||||
"""
|
|
||||||
|
|
||||||
def process(self, title: str, subtitle: str = None) -> Optional[Context]:
|
|
||||||
"""
|
|
||||||
识别媒体信息
|
|
||||||
"""
|
|
||||||
logger.info(f'开始识别媒体信息,标题:{title},副标题:{subtitle} ...')
|
|
||||||
# 识别前预处理
|
|
||||||
result: Optional[tuple] = self.prepare_recognize(title=title, subtitle=subtitle)
|
|
||||||
if result:
|
|
||||||
title, subtitle = result
|
|
||||||
# 识别元数据
|
|
||||||
metainfo = MetaInfo(title, subtitle)
|
|
||||||
# 识别媒体信息
|
|
||||||
mediainfo: MediaInfo = self.recognize_media(meta=metainfo)
|
|
||||||
if not mediainfo:
|
|
||||||
logger.warn(f'{title} 未识别到媒体信息')
|
|
||||||
return Context(meta=metainfo)
|
|
||||||
logger.info(f'{title} 识别到媒体信息:{mediainfo.type.value} {mediainfo.title_year}')
|
|
||||||
# 更新媒体图片
|
|
||||||
self.obtain_image(mediainfo=mediainfo)
|
|
||||||
# 返回上下文
|
|
||||||
return Context(meta=metainfo, mediainfo=mediainfo, title=title, subtitle=subtitle)
|
|
68
app/chain/media.py
Normal file
68
app/chain/media.py
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
from typing import Optional, List, Tuple
|
||||||
|
|
||||||
|
from app.chain import ChainBase
|
||||||
|
from app.core.context import Context, MediaInfo
|
||||||
|
from app.core.meta import MetaBase
|
||||||
|
from app.core.metainfo import MetaInfo
|
||||||
|
from app.log import logger
|
||||||
|
from app.utils.string import StringUtils
|
||||||
|
|
||||||
|
|
||||||
|
class MediaChain(ChainBase):
|
||||||
|
"""
|
||||||
|
识别处理链
|
||||||
|
"""
|
||||||
|
|
||||||
|
def recognize_by_title(self, title: str, subtitle: str = None) -> Optional[Context]:
|
||||||
|
"""
|
||||||
|
识别媒体信息
|
||||||
|
"""
|
||||||
|
logger.info(f'开始识别媒体信息,标题:{title},副标题:{subtitle} ...')
|
||||||
|
# 识别前预处理
|
||||||
|
result: Optional[tuple] = self.prepare_recognize(title=title, subtitle=subtitle)
|
||||||
|
if result:
|
||||||
|
title, subtitle = result
|
||||||
|
# 识别元数据
|
||||||
|
metainfo = MetaInfo(title, subtitle)
|
||||||
|
# 识别媒体信息
|
||||||
|
mediainfo: MediaInfo = self.recognize_media(meta=metainfo)
|
||||||
|
if not mediainfo:
|
||||||
|
logger.warn(f'{title} 未识别到媒体信息')
|
||||||
|
return Context(meta=metainfo)
|
||||||
|
logger.info(f'{title} 识别到媒体信息:{mediainfo.type.value} {mediainfo.title_year}')
|
||||||
|
# 更新媒体图片
|
||||||
|
self.obtain_image(mediainfo=mediainfo)
|
||||||
|
# 返回上下文
|
||||||
|
return Context(meta=metainfo, mediainfo=mediainfo, title=title, subtitle=subtitle)
|
||||||
|
|
||||||
|
def search(self, title: str) -> Tuple[MetaBase, List[MediaInfo]]:
|
||||||
|
"""
|
||||||
|
搜索媒体信息
|
||||||
|
:param title: 搜索内容
|
||||||
|
:return: 识别元数据,媒体信息列表
|
||||||
|
"""
|
||||||
|
# 提取要素
|
||||||
|
mtype, key_word, season_num, episode_num, year, content = StringUtils.get_keyword(title)
|
||||||
|
# 识别
|
||||||
|
meta = MetaInfo(content)
|
||||||
|
if not meta.name:
|
||||||
|
logger.warn(f'{title} 未识别到元数据!')
|
||||||
|
return meta, []
|
||||||
|
# 合并信息
|
||||||
|
if mtype:
|
||||||
|
meta.type = mtype
|
||||||
|
if season_num:
|
||||||
|
meta.begin_season = season_num
|
||||||
|
if episode_num:
|
||||||
|
meta.begin_episode = episode_num
|
||||||
|
if year:
|
||||||
|
meta.year = year
|
||||||
|
# 开始搜索
|
||||||
|
logger.info(f"开始搜索媒体信息:{meta.name}")
|
||||||
|
medias: Optional[List[MediaInfo]] = self.search_medias(meta=meta)
|
||||||
|
if not medias:
|
||||||
|
logger.warn(f"{meta.name} 没有找到对应的媒体信息!")
|
||||||
|
return meta, []
|
||||||
|
logger.info(f"{content} 搜索到 {len(medias)} 条相关媒体信息")
|
||||||
|
# 识别的元数据,媒体信息列表
|
||||||
|
return meta, medias
|
@ -1,16 +1,16 @@
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from app.chain.download import *
|
from app.chain.download import *
|
||||||
|
from app.chain.media import MediaChain
|
||||||
from app.chain.search import SearchChain
|
from app.chain.search import SearchChain
|
||||||
from app.chain.subscribe import SubscribeChain
|
from app.chain.subscribe import SubscribeChain
|
||||||
from app.core.context import MediaInfo
|
from app.core.context import MediaInfo
|
||||||
from app.core.event import EventManager
|
from app.core.event import EventManager
|
||||||
from app.core.metainfo import MetaInfo
|
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.schemas.types import EventType
|
from app.schemas.types import EventType
|
||||||
|
|
||||||
|
|
||||||
class UserMessageChain(ChainBase):
|
class MessageChain(ChainBase):
|
||||||
"""
|
"""
|
||||||
外来消息处理链
|
外来消息处理链
|
||||||
"""
|
"""
|
||||||
@ -30,6 +30,7 @@ class UserMessageChain(ChainBase):
|
|||||||
self.downloadchain = DownloadChain()
|
self.downloadchain = DownloadChain()
|
||||||
self.subscribechain = SubscribeChain()
|
self.subscribechain = SubscribeChain()
|
||||||
self.searchchain = SearchChain()
|
self.searchchain = SearchChain()
|
||||||
|
self.medtachain = MediaChain()
|
||||||
self.torrent = TorrentHelper()
|
self.torrent = TorrentHelper()
|
||||||
self.eventmanager = EventManager()
|
self.eventmanager = EventManager()
|
||||||
|
|
||||||
@ -93,7 +94,8 @@ class UserMessageChain(ChainBase):
|
|||||||
# 发送缺失的媒体信息
|
# 发送缺失的媒体信息
|
||||||
if no_exists:
|
if no_exists:
|
||||||
# 发送消息
|
# 发送消息
|
||||||
messages = [f"第 {no_exist.get('season')} 季缺失 {len(no_exist.get('episodes')) or no_exist.get('total_episodes')} 集"
|
messages = [
|
||||||
|
f"第 {no_exist.get('season')} 季缺失 {len(no_exist.get('episodes')) or no_exist.get('total_episodes')} 集"
|
||||||
for no_exist in no_exists.get(mediainfo.tmdb_id)]
|
for no_exist in no_exists.get(mediainfo.tmdb_id)]
|
||||||
self.post_message(title=f"{mediainfo.title_year}:\n" + "\n".join(messages))
|
self.post_message(title=f"{mediainfo.title_year}:\n" + "\n".join(messages))
|
||||||
# 搜索种子,过滤掉不需要的剧集,以便选择
|
# 搜索种子,过滤掉不需要的剧集,以便选择
|
||||||
@ -101,8 +103,7 @@ class UserMessageChain(ChainBase):
|
|||||||
self.post_message(
|
self.post_message(
|
||||||
title=f"开始搜索 {mediainfo.type.value} {mediainfo.title_year} ...", userid=userid)
|
title=f"开始搜索 {mediainfo.type.value} {mediainfo.title_year} ...", userid=userid)
|
||||||
# 开始搜索
|
# 开始搜索
|
||||||
contexts = self.searchchain.process(meta=self._current_meta,
|
contexts = self.searchchain.process(mediainfo=mediainfo,
|
||||||
mediainfo=mediainfo,
|
|
||||||
no_exists=no_exists)
|
no_exists=no_exists)
|
||||||
if not contexts:
|
if not contexts:
|
||||||
# 没有数据
|
# 没有数据
|
||||||
@ -129,7 +130,7 @@ class UserMessageChain(ChainBase):
|
|||||||
elif cache_type == "Subscribe":
|
elif cache_type == "Subscribe":
|
||||||
# 订阅媒体
|
# 订阅媒体
|
||||||
mediainfo: MediaInfo = cache_list[int(text) - 1]
|
mediainfo: MediaInfo = cache_list[int(text) - 1]
|
||||||
self.subscribechain.process(title=mediainfo.title,
|
self.subscribechain.add(title=mediainfo.title,
|
||||||
year=mediainfo.year,
|
year=mediainfo.year,
|
||||||
mtype=mediainfo.type,
|
mtype=mediainfo.type,
|
||||||
tmdbid=mediainfo.tmdb_id,
|
tmdbid=mediainfo.tmdb_id,
|
||||||
@ -158,7 +159,7 @@ class UserMessageChain(ChainBase):
|
|||||||
# 未完成下载
|
# 未完成下载
|
||||||
logger.info(f'{self._current_media.title_year} 未下载未完整,添加订阅 ...')
|
logger.info(f'{self._current_media.title_year} 未下载未完整,添加订阅 ...')
|
||||||
# 添加订阅
|
# 添加订阅
|
||||||
self.subscribechain.process(title=self._current_media.title,
|
self.subscribechain.add(title=self._current_media.title,
|
||||||
year=self._current_media.year,
|
year=self._current_media.year,
|
||||||
mtype=self._current_media.type,
|
mtype=self._current_media.type,
|
||||||
tmdbid=self._current_media.tmdb_id,
|
tmdbid=self._current_media.tmdb_id,
|
||||||
@ -245,31 +246,19 @@ class UserMessageChain(ChainBase):
|
|||||||
# 搜索
|
# 搜索
|
||||||
content = re.sub(r"(搜索|下载)[::\s]*", "", text)
|
content = re.sub(r"(搜索|下载)[::\s]*", "", text)
|
||||||
action = "Search"
|
action = "Search"
|
||||||
# 提取要素
|
# 搜索
|
||||||
mtype, key_word, season_num, episode_num, year, title = StringUtils.get_keyword(content)
|
meta, medias = self.medtachain.search(content)
|
||||||
# 识别
|
# 识别
|
||||||
meta = MetaInfo(title)
|
|
||||||
if not meta.name:
|
if not meta.name:
|
||||||
self.post_message(title="无法识别输入内容!", userid=userid)
|
self.post_message(title="无法识别输入内容!", userid=userid)
|
||||||
return
|
return
|
||||||
# 合并信息
|
|
||||||
if mtype:
|
|
||||||
meta.type = mtype
|
|
||||||
if season_num:
|
|
||||||
meta.begin_season = season_num
|
|
||||||
if episode_num:
|
|
||||||
meta.begin_episode = episode_num
|
|
||||||
if year:
|
|
||||||
meta.year = year
|
|
||||||
# 记录当前状态
|
|
||||||
self._current_meta = meta
|
|
||||||
# 开始搜索
|
# 开始搜索
|
||||||
logger.info(f"开始搜索:{meta.name}")
|
|
||||||
medias: Optional[List[MediaInfo]] = self.search_medias(meta=meta)
|
|
||||||
if not medias:
|
if not medias:
|
||||||
self.post_message(title=f"{meta.name} 没有找到对应的媒体信息!", userid=userid)
|
self.post_message(title=f"{meta.name} 没有找到对应的媒体信息!", userid=userid)
|
||||||
return
|
return
|
||||||
logger.info(f"搜索到 {len(medias)} 条相关媒体信息")
|
logger.info(f"搜索到 {len(medias)} 条相关媒体信息")
|
||||||
|
# 记录当前状态
|
||||||
|
self._current_meta = meta
|
||||||
self._user_cache[userid] = {
|
self._user_cache[userid] = {
|
||||||
'type': action,
|
'type': action,
|
||||||
'items': medias
|
'items': medias
|
@ -3,13 +3,12 @@ from typing import Optional, List, Dict
|
|||||||
from app.chain import ChainBase
|
from app.chain import ChainBase
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
from app.core.context import Context, MediaInfo, TorrentInfo
|
from app.core.context import Context, MediaInfo, TorrentInfo
|
||||||
from app.core.meta import MetaBase
|
|
||||||
from app.core.metainfo import MetaInfo
|
from app.core.metainfo import MetaInfo
|
||||||
from app.helper.sites import SitesHelper
|
from app.helper.sites import SitesHelper
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.schemas.context import NotExistMediaInfo
|
from app.schemas import NotExistMediaInfo
|
||||||
from app.utils.string import StringUtils
|
|
||||||
from app.schemas.types import MediaType
|
from app.schemas.types import MediaType
|
||||||
|
from app.utils.string import StringUtils
|
||||||
|
|
||||||
|
|
||||||
class SearchChain(ChainBase):
|
class SearchChain(ChainBase):
|
||||||
@ -21,12 +20,23 @@ class SearchChain(ChainBase):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
self.siteshelper = SitesHelper()
|
self.siteshelper = SitesHelper()
|
||||||
|
|
||||||
def process(self, meta: MetaBase, mediainfo: MediaInfo,
|
def search_by_tmdbid(self, tmdbid: int, mtype: str = None) -> Optional[List[Context]]:
|
||||||
|
"""
|
||||||
|
根据TMDB ID搜索资源,不过滤本地存在的内容
|
||||||
|
:param tmdbid: TMDB ID
|
||||||
|
:param mtype: 媒体,电影 or 电视剧
|
||||||
|
"""
|
||||||
|
mediainfo = self.recognize_media(tmdbid=tmdbid, mtype=mtype)
|
||||||
|
if not mediainfo:
|
||||||
|
logger.error(f'{tmdbid} 媒体信息识别失败!')
|
||||||
|
return None
|
||||||
|
return self.process(mediainfo=mediainfo)
|
||||||
|
|
||||||
|
def process(self, mediainfo: MediaInfo,
|
||||||
keyword: str = None,
|
keyword: str = None,
|
||||||
no_exists: Dict[int, Dict[int, NotExistMediaInfo]] = None) -> Optional[List[Context]]:
|
no_exists: Dict[int, Dict[int, NotExistMediaInfo]] = None) -> Optional[List[Context]]:
|
||||||
"""
|
"""
|
||||||
根据媒体信息,执行搜索
|
根据媒体信息,搜索种子资源
|
||||||
:param meta: 元数据
|
|
||||||
:param mediainfo: 媒体信息
|
:param mediainfo: 媒体信息
|
||||||
:param keyword: 搜索关键词
|
:param keyword: 搜索关键词
|
||||||
:param no_exists: 缺失的媒体信息
|
:param no_exists: 缺失的媒体信息
|
||||||
@ -124,6 +134,6 @@ class SearchChain(ChainBase):
|
|||||||
_match_torrents = torrents
|
_match_torrents = torrents
|
||||||
logger.info(f"匹配完成,共匹配到 {len(_match_torrents)} 个资源")
|
logger.info(f"匹配完成,共匹配到 {len(_match_torrents)} 个资源")
|
||||||
# 组装上下文返回
|
# 组装上下文返回
|
||||||
return [Context(meta=MetaInfo(torrent.title),
|
return [Context(meta=MetaInfo(title=torrent.title, subtitle=torrent.description),
|
||||||
mediainfo=mediainfo,
|
mediainfo=mediainfo,
|
||||||
torrentinfo=torrent) for torrent in _match_torrents]
|
torrentinfo=torrent) for torrent in _match_torrents]
|
||||||
|
@ -7,7 +7,7 @@ from app.helper.cookie import CookieHelper
|
|||||||
from app.log import logger
|
from app.log import logger
|
||||||
|
|
||||||
|
|
||||||
class SiteMessageChain(ChainBase):
|
class SiteChain(ChainBase):
|
||||||
"""
|
"""
|
||||||
站点远程管理处理链
|
站点远程管理处理链
|
||||||
"""
|
"""
|
||||||
@ -20,7 +20,7 @@ class SiteMessageChain(ChainBase):
|
|||||||
self._siteoper = SiteOper()
|
self._siteoper = SiteOper()
|
||||||
self._cookiehelper = CookieHelper()
|
self._cookiehelper = CookieHelper()
|
||||||
|
|
||||||
def process(self, userid: Union[str, int] = None):
|
def list(self, userid: Union[str, int] = None):
|
||||||
"""
|
"""
|
||||||
查询所有站点,发送消息
|
查询所有站点,发送消息
|
||||||
"""
|
"""
|
||||||
@ -63,7 +63,7 @@ class SiteMessageChain(ChainBase):
|
|||||||
"is_active": False
|
"is_active": False
|
||||||
})
|
})
|
||||||
# 重新发送消息
|
# 重新发送消息
|
||||||
self.process()
|
self.list()
|
||||||
|
|
||||||
def enable(self, arg_str, userid: Union[str, int] = None):
|
def enable(self, arg_str, userid: Union[str, int] = None):
|
||||||
"""
|
"""
|
||||||
@ -84,7 +84,7 @@ class SiteMessageChain(ChainBase):
|
|||||||
"is_active": True
|
"is_active": True
|
||||||
})
|
})
|
||||||
# 重新发送消息
|
# 重新发送消息
|
||||||
self.process()
|
self.list()
|
||||||
|
|
||||||
def get_cookie(self, arg_str: str, userid: Union[str, int] = None):
|
def get_cookie(self, arg_str: str, userid: Union[str, int] = None):
|
||||||
"""
|
"""
|
@ -9,7 +9,7 @@ from app.core.config import settings
|
|||||||
from app.db.subscribe_oper import SubscribeOper
|
from app.db.subscribe_oper import SubscribeOper
|
||||||
from app.helper.sites import SitesHelper
|
from app.helper.sites import SitesHelper
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.schemas.context import NotExistMediaInfo
|
from app.schemas import NotExistMediaInfo
|
||||||
from app.utils.string import StringUtils
|
from app.utils.string import StringUtils
|
||||||
from app.schemas.types import MediaType
|
from app.schemas.types import MediaType
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ class SubscribeChain(ChainBase):
|
|||||||
self.subscribehelper = SubscribeOper()
|
self.subscribehelper = SubscribeOper()
|
||||||
self.siteshelper = SitesHelper()
|
self.siteshelper = SitesHelper()
|
||||||
|
|
||||||
def process(self, title: str, year: str,
|
def add(self, title: str, year: str,
|
||||||
mtype: MediaType = None,
|
mtype: MediaType = None,
|
||||||
tmdbid: int = None,
|
tmdbid: int = None,
|
||||||
season: int = None,
|
season: int = None,
|
||||||
@ -153,8 +153,7 @@ class SubscribeChain(ChainBase):
|
|||||||
|
|
||||||
)
|
)
|
||||||
# 搜索
|
# 搜索
|
||||||
contexts = self.searchchain.process(meta=meta,
|
contexts = self.searchchain.process(mediainfo=mediainfo,
|
||||||
mediainfo=mediainfo,
|
|
||||||
keyword=subscribe.keyword,
|
keyword=subscribe.keyword,
|
||||||
no_exists=no_exists)
|
no_exists=no_exists)
|
||||||
if not contexts:
|
if not contexts:
|
||||||
|
@ -9,7 +9,7 @@ from app.core.metainfo import MetaInfo
|
|||||||
from app.db.downloadhistory_oper import DownloadHistoryOper
|
from app.db.downloadhistory_oper import DownloadHistoryOper
|
||||||
from app.db.models.downloadhistory import DownloadHistory
|
from app.db.models.downloadhistory import DownloadHistory
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.schemas.context import TransferInfo, TransferTorrent
|
from app.schemas import TransferInfo, TransferTorrent
|
||||||
from app.schemas.types import TorrentStatus, EventType, MediaType
|
from app.schemas.types import TorrentStatus, EventType, MediaType
|
||||||
from app.utils.string import StringUtils
|
from app.utils.string import StringUtils
|
||||||
|
|
||||||
|
@ -6,12 +6,12 @@ from app.utils.http import WebUtils
|
|||||||
from app.schemas.types import EventType
|
from app.schemas.types import EventType
|
||||||
|
|
||||||
|
|
||||||
class WebhookMessageChain(ChainBase):
|
class WebhookChain(ChainBase):
|
||||||
"""
|
"""
|
||||||
响应Webhook事件
|
响应Webhook事件
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def process(self, body: Any, form: Any, args: Any) -> None:
|
def message(self, body: Any, form: Any, args: Any) -> None:
|
||||||
"""
|
"""
|
||||||
处理Webhook报文并发送消息
|
处理Webhook报文并发送消息
|
||||||
"""
|
"""
|
@ -4,9 +4,9 @@ from typing import Any, Union
|
|||||||
|
|
||||||
from app.chain import ChainBase
|
from app.chain import ChainBase
|
||||||
from app.chain.cookiecloud import CookieCloudChain
|
from app.chain.cookiecloud import CookieCloudChain
|
||||||
from app.chain.douban_sync import DoubanSyncChain
|
from app.chain.douban import DoubanChain
|
||||||
from app.chain.download import DownloadChain
|
from app.chain.download import DownloadChain
|
||||||
from app.chain.site_message import SiteMessageChain
|
from app.chain.site import SiteChain
|
||||||
from app.chain.subscribe import SubscribeChain
|
from app.chain.subscribe import SubscribeChain
|
||||||
from app.chain.transfer import TransferChain
|
from app.chain.transfer import TransferChain
|
||||||
from app.core.event import eventmanager, EventManager
|
from app.core.event import eventmanager, EventManager
|
||||||
@ -50,27 +50,27 @@ class Command(metaclass=Singleton):
|
|||||||
"data": {}
|
"data": {}
|
||||||
},
|
},
|
||||||
"/sites": {
|
"/sites": {
|
||||||
"func": SiteMessageChain().process,
|
"func": SiteChain().list,
|
||||||
"description": "查询站点",
|
"description": "查询站点",
|
||||||
"data": {}
|
"data": {}
|
||||||
},
|
},
|
||||||
"/site_cookie": {
|
"/site_cookie": {
|
||||||
"func": SiteMessageChain().get_cookie,
|
"func": SiteChain().get_cookie,
|
||||||
"description": "更新站点Cookie",
|
"description": "更新站点Cookie",
|
||||||
"data": {}
|
"data": {}
|
||||||
},
|
},
|
||||||
"/site_enable": {
|
"/site_enable": {
|
||||||
"func": SiteMessageChain().enable,
|
"func": SiteChain().enable,
|
||||||
"description": "启用站点",
|
"description": "启用站点",
|
||||||
"data": {}
|
"data": {}
|
||||||
},
|
},
|
||||||
"/site_disable": {
|
"/site_disable": {
|
||||||
"func": SiteMessageChain().disable,
|
"func": SiteChain().disable,
|
||||||
"description": "禁用站点",
|
"description": "禁用站点",
|
||||||
"data": {}
|
"data": {}
|
||||||
},
|
},
|
||||||
"/douban_sync": {
|
"/douban_sync": {
|
||||||
"func": DoubanSyncChain().process,
|
"func": DoubanChain().sync,
|
||||||
"description": "同步豆瓣想看",
|
"description": "同步豆瓣想看",
|
||||||
"data": {}
|
"data": {}
|
||||||
},
|
},
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from typing import Optional, Any, List
|
import re
|
||||||
|
from typing import Optional, Any, List, Dict
|
||||||
|
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
from app.core.meta import MetaBase
|
from app.core.meta import MetaBase
|
||||||
@ -121,7 +122,7 @@ class MediaInfo:
|
|||||||
# 所有别名和译名
|
# 所有别名和译名
|
||||||
names: Optional[list] = []
|
names: Optional[list] = []
|
||||||
# 各季的剧集清单信息
|
# 各季的剧集清单信息
|
||||||
seasons: Optional[dict] = {}
|
seasons: Optional[Dict[int, list]] = {}
|
||||||
# 各季的年份
|
# 各季的年份
|
||||||
season_years: Optional[dict] = {}
|
season_years: Optional[dict] = {}
|
||||||
# 二级分类
|
# 二级分类
|
||||||
@ -302,44 +303,61 @@ class MediaInfo:
|
|||||||
self.douban_info = info
|
self.douban_info = info
|
||||||
# 豆瓣ID
|
# 豆瓣ID
|
||||||
self.douban_id = str(info.get("id"))
|
self.douban_id = str(info.get("id"))
|
||||||
|
# 类型
|
||||||
|
if not self.type:
|
||||||
|
self.type = MediaType.MOVIE if info.get("type") == "movie" else MediaType.TV
|
||||||
|
# 标题
|
||||||
|
if not self.title:
|
||||||
|
self.title = MetaInfo(info.get("title")).name
|
||||||
|
# 原语种标题
|
||||||
|
if not self.original_title:
|
||||||
|
self.original_title = info.get("original_title")
|
||||||
|
# 年份
|
||||||
|
if not self.year:
|
||||||
|
self.year = info.get("year")[:4] if info.get("year") else None
|
||||||
# 评分
|
# 评分
|
||||||
if not self.vote_average:
|
if not self.vote_average:
|
||||||
rating = info.get('rating')
|
rating = info.get("rating")
|
||||||
if rating:
|
if rating:
|
||||||
vote_average = float(rating.get("value"))
|
vote_average = float(rating.get("value"))
|
||||||
else:
|
else:
|
||||||
vote_average = 0
|
vote_average = 0
|
||||||
self.vote_average = vote_average
|
self.vote_average = vote_average
|
||||||
# 标题
|
# 发行日期
|
||||||
if not self.title:
|
if not self.release_date:
|
||||||
self.title = info.get('title')
|
if info.get("release_date"):
|
||||||
# 年份
|
self.release_date = info.get("release_date")
|
||||||
if not self.year:
|
elif info.get("pubdate") and isinstance(info.get("pubdate"), list):
|
||||||
self.year = info.get('year')[:4] if info.get('year') else None
|
release_date = info.get("pubdate")[0]
|
||||||
# 原语种标题
|
if release_date:
|
||||||
if not self.original_title:
|
match = re.search(r'\d{4}-\d{2}-\d{2}', release_date)
|
||||||
self.original_title = info.get("original_title")
|
if match:
|
||||||
# 类型
|
self.release_date = match.group()
|
||||||
if not self.type:
|
# 海报
|
||||||
self.type = MediaType.MOVIE if info.get("type") == "movie" else MediaType.TV
|
|
||||||
if not self.poster_path:
|
if not self.poster_path:
|
||||||
if self.type == MediaType.MOVIE:
|
self.poster_path = info.get("pic", {}).get("large")
|
||||||
# 海报
|
if not self.poster_path and info.get("cover_url"):
|
||||||
poster_path = info.get('cover', {}).get("url")
|
self.poster_path = info.get("cover_url")
|
||||||
if not poster_path:
|
if self.poster_path:
|
||||||
poster_path = info.get('cover_url')
|
self.poster_path = self.poster_path.replace("m_ratio_poster", "l_ratio_poster")
|
||||||
if not poster_path:
|
|
||||||
poster_path = info.get('pic', {}).get("large")
|
|
||||||
else:
|
|
||||||
# 海报
|
|
||||||
poster_path = info.get('pic', {}).get("normal")
|
|
||||||
self.poster_path = poster_path
|
|
||||||
# 简介
|
# 简介
|
||||||
if not self.overview:
|
if not self.overview:
|
||||||
overview = info.get("card_subtitle") or ""
|
self.overview = info.get("intro") or info.get("card_subtitle") or ""
|
||||||
if not self.year and overview:
|
# 导演和演员
|
||||||
if overview.split("/")[0].strip().isdigit():
|
if not self.directors:
|
||||||
self.year = overview.split("/")[0].strip()
|
self.directors = info.get("directors") or []
|
||||||
|
if not self.actors:
|
||||||
|
self.actors = info.get("actors") or []
|
||||||
|
# 别名
|
||||||
|
if not self.names:
|
||||||
|
self.names = info.get("aka") or []
|
||||||
|
# 剧集
|
||||||
|
if self.type == MediaType.TV and not self.seasons:
|
||||||
|
meta = MetaInfo(info.get("title"))
|
||||||
|
if meta.begin_season:
|
||||||
|
episodes_count = info.get("episodes_count")
|
||||||
|
if episodes_count:
|
||||||
|
self.seasons[meta.begin_season] = list(range(1, episodes_count + 1))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def title_year(self):
|
def title_year(self):
|
||||||
@ -420,6 +438,20 @@ class MediaInfo:
|
|||||||
return []
|
return []
|
||||||
return self.seasons.get(sea) or []
|
return self.seasons.get(sea) or []
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
"""
|
||||||
|
返回字典
|
||||||
|
"""
|
||||||
|
attributes = [
|
||||||
|
attr for attr in dir(self)
|
||||||
|
if not callable(getattr(self, attr)) and not attr.startswith("_")
|
||||||
|
]
|
||||||
|
return {
|
||||||
|
attr: getattr(self, attr).value
|
||||||
|
if isinstance(getattr(self, attr), MediaType)
|
||||||
|
else getattr(self, attr) for attr in attributes
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class Context:
|
class Context:
|
||||||
"""
|
"""
|
||||||
@ -505,5 +537,6 @@ class Context:
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
"meta_info": object_to_dict(self.meta_info),
|
"meta_info": object_to_dict(self.meta_info),
|
||||||
"media_info": object_to_dict(self.media_info)
|
"media_info": object_to_dict(self.media_info),
|
||||||
|
"torrent_info": object_to_dict(self.torrent_info)
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ from ruamel.yaml import CommentedMap
|
|||||||
|
|
||||||
from app.core.context import MediaInfo, TorrentInfo, Context
|
from app.core.context import MediaInfo, TorrentInfo, Context
|
||||||
from app.core.meta import MetaBase
|
from app.core.meta import MetaBase
|
||||||
from app.schemas.context import TransferInfo, TransferTorrent, ExistMediaInfo, DownloadingTorrent
|
from app.schemas import TransferInfo, TransferTorrent, ExistMediaInfo, DownloadingTorrent
|
||||||
from app.schemas.types import TorrentStatus, MediaType
|
from app.schemas.types import TorrentStatus, MediaType
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ from app.core.context import MediaInfo
|
|||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.modules import _ModuleBase
|
from app.modules import _ModuleBase
|
||||||
from app.modules.emby.emby import Emby
|
from app.modules.emby.emby import Emby
|
||||||
from app.schemas.context import ExistMediaInfo, RefreshMediaItem
|
from app.schemas import ExistMediaInfo, RefreshMediaItem
|
||||||
from app.schemas.types import MediaType
|
from app.schemas.types import MediaType
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ from typing import List, Optional, Union, Dict
|
|||||||
|
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.schemas.context import RefreshMediaItem
|
from app.schemas import RefreshMediaItem
|
||||||
from app.utils.http import RequestUtils
|
from app.utils.http import RequestUtils
|
||||||
from app.utils.singleton import Singleton
|
from app.utils.singleton import Singleton
|
||||||
from app.utils.string import StringUtils
|
from app.utils.string import StringUtils
|
||||||
|
@ -11,7 +11,7 @@ from app.core.config import settings
|
|||||||
from app.core.meta import MetaBase
|
from app.core.meta import MetaBase
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.modules import _ModuleBase
|
from app.modules import _ModuleBase
|
||||||
from app.schemas.context import TransferInfo
|
from app.schemas import TransferInfo
|
||||||
from app.utils.system import SystemUtils
|
from app.utils.system import SystemUtils
|
||||||
from app.schemas.types import MediaType
|
from app.schemas.types import MediaType
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ from app.core.context import MediaInfo
|
|||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.modules import _ModuleBase
|
from app.modules import _ModuleBase
|
||||||
from app.modules.jellyfin.jellyfin import Jellyfin
|
from app.modules.jellyfin.jellyfin import Jellyfin
|
||||||
from app.schemas.context import ExistMediaInfo
|
from app.schemas import ExistMediaInfo
|
||||||
from app.schemas.types import MediaType
|
from app.schemas.types import MediaType
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ from app.core.context import MediaInfo
|
|||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.modules import _ModuleBase
|
from app.modules import _ModuleBase
|
||||||
from app.modules.plex.plex import Plex
|
from app.modules.plex.plex import Plex
|
||||||
from app.schemas.context import ExistMediaInfo, RefreshMediaItem
|
from app.schemas import ExistMediaInfo, RefreshMediaItem
|
||||||
from app.schemas.types import MediaType
|
from app.schemas.types import MediaType
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ from plexapi.server import PlexServer
|
|||||||
|
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.schemas.context import RefreshMediaItem
|
from app.schemas import RefreshMediaItem
|
||||||
from app.utils.singleton import Singleton
|
from app.utils.singleton import Singleton
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ from app.core.metainfo import MetaInfo
|
|||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.modules import _ModuleBase
|
from app.modules import _ModuleBase
|
||||||
from app.modules.qbittorrent.qbittorrent import Qbittorrent
|
from app.modules.qbittorrent.qbittorrent import Qbittorrent
|
||||||
from app.schemas.context import TransferInfo, TransferTorrent, DownloadingTorrent
|
from app.schemas import TransferInfo, TransferTorrent, DownloadingTorrent
|
||||||
from app.utils.string import StringUtils
|
from app.utils.string import StringUtils
|
||||||
from app.schemas.types import TorrentStatus
|
from app.schemas.types import TorrentStatus
|
||||||
|
|
||||||
|
@ -538,14 +538,14 @@ class TmdbHelper:
|
|||||||
tmdb_info = self.__get_tv_detail(tmdbid)
|
tmdb_info = self.__get_tv_detail(tmdbid)
|
||||||
if tmdb_info:
|
if tmdb_info:
|
||||||
tmdb_info['media_type'] = MediaType.TV
|
tmdb_info['media_type'] = MediaType.TV
|
||||||
else:
|
|
||||||
tmdb_info = self.__get_movie_detail(tmdbid)
|
|
||||||
if tmdb_info:
|
|
||||||
tmdb_info['media_type'] = MediaType.MOVIE
|
|
||||||
else:
|
else:
|
||||||
tmdb_info = self.__get_tv_detail(tmdbid)
|
tmdb_info = self.__get_tv_detail(tmdbid)
|
||||||
if tmdb_info:
|
if tmdb_info:
|
||||||
tmdb_info['media_type'] = MediaType.TV
|
tmdb_info['media_type'] = MediaType.TV
|
||||||
|
else:
|
||||||
|
tmdb_info = self.__get_movie_detail(tmdbid)
|
||||||
|
if tmdb_info:
|
||||||
|
tmdb_info['media_type'] = MediaType.MOVIE
|
||||||
|
|
||||||
if tmdb_info:
|
if tmdb_info:
|
||||||
# 转换genreid
|
# 转换genreid
|
||||||
|
@ -6,7 +6,7 @@ from app.core.metainfo import MetaInfo
|
|||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.modules import _ModuleBase
|
from app.modules import _ModuleBase
|
||||||
from app.modules.transmission.transmission import Transmission
|
from app.modules.transmission.transmission import Transmission
|
||||||
from app.schemas.context import TransferInfo, TransferTorrent, DownloadingTorrent
|
from app.schemas import TransferInfo, TransferTorrent, DownloadingTorrent
|
||||||
from app.schemas.types import TorrentStatus
|
from app.schemas.types import TorrentStatus
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ from typing import Tuple
|
|||||||
|
|
||||||
from ruamel.yaml import CommentedMap
|
from ruamel.yaml import CommentedMap
|
||||||
|
|
||||||
from app.core.config import settings
|
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.plugins.autosignin.sites import _ISiteSigninHandler
|
from app.plugins.autosignin.sites import _ISiteSigninHandler
|
||||||
from app.utils.string import StringUtils
|
from app.utils.string import StringUtils
|
||||||
|
@ -2,7 +2,6 @@ from typing import Tuple
|
|||||||
|
|
||||||
from ruamel.yaml import CommentedMap
|
from ruamel.yaml import CommentedMap
|
||||||
|
|
||||||
from app.core.config import settings
|
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.plugins.autosignin.sites import _ISiteSigninHandler
|
from app.plugins.autosignin.sites import _ISiteSigninHandler
|
||||||
from app.utils.string import StringUtils
|
from app.utils.string import StringUtils
|
||||||
|
@ -2,7 +2,6 @@ from typing import Tuple
|
|||||||
|
|
||||||
from ruamel.yaml import CommentedMap
|
from ruamel.yaml import CommentedMap
|
||||||
|
|
||||||
from app.core.config import settings
|
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.plugins.autosignin.sites import _ISiteSigninHandler
|
from app.plugins.autosignin.sites import _ISiteSigninHandler
|
||||||
from app.utils.string import StringUtils
|
from app.utils.string import StringUtils
|
||||||
|
@ -3,7 +3,6 @@ from typing import Tuple
|
|||||||
|
|
||||||
from ruamel.yaml import CommentedMap
|
from ruamel.yaml import CommentedMap
|
||||||
|
|
||||||
from app.core.config import settings
|
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.plugins.autosignin.sites import _ISiteSigninHandler
|
from app.plugins.autosignin.sites import _ISiteSigninHandler
|
||||||
from app.utils.string import StringUtils
|
from app.utils.string import StringUtils
|
||||||
|
@ -3,7 +3,6 @@ from typing import Tuple
|
|||||||
|
|
||||||
from ruamel.yaml import CommentedMap
|
from ruamel.yaml import CommentedMap
|
||||||
|
|
||||||
from app.core.config import settings
|
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.plugins.autosignin.sites import _ISiteSigninHandler
|
from app.plugins.autosignin.sites import _ISiteSigninHandler
|
||||||
from app.utils.string import StringUtils
|
from app.utils.string import StringUtils
|
||||||
|
@ -6,7 +6,7 @@ from apscheduler.executors.pool import ThreadPoolExecutor
|
|||||||
from apscheduler.schedulers.background import BackgroundScheduler
|
from apscheduler.schedulers.background import BackgroundScheduler
|
||||||
|
|
||||||
from app.chain.cookiecloud import CookieCloudChain
|
from app.chain.cookiecloud import CookieCloudChain
|
||||||
from app.chain.douban_sync import DoubanSyncChain
|
from app.chain.douban import DoubanChain
|
||||||
from app.chain.subscribe import SubscribeChain
|
from app.chain.subscribe import SubscribeChain
|
||||||
from app.chain.transfer import TransferChain
|
from app.chain.transfer import TransferChain
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
@ -51,7 +51,7 @@ class Scheduler(metaclass=Singleton):
|
|||||||
self._scheduler.add_job(SubscribeChain().refresh, "cron", hour=trigger.hour, minute=trigger.minute)
|
self._scheduler.add_job(SubscribeChain().refresh, "cron", hour=trigger.hour, minute=trigger.minute)
|
||||||
|
|
||||||
# 豆瓣同步(每30分钟)
|
# 豆瓣同步(每30分钟)
|
||||||
self._scheduler.add_job(DoubanSyncChain().process, "interval", minutes=30)
|
self._scheduler.add_job(DoubanChain().sync, "interval", minutes=30)
|
||||||
|
|
||||||
# 下载器文件转移(每5分钟)
|
# 下载器文件转移(每5分钟)
|
||||||
self._scheduler.add_job(TransferChain().process, "interval", minutes=5)
|
self._scheduler.add_job(TransferChain().process, "interval", minutes=5)
|
||||||
|
@ -3,5 +3,6 @@ from .user import User, UserCreate, UserInDB, UserUpdate
|
|||||||
from .response import Response
|
from .response import Response
|
||||||
from .site import Site
|
from .site import Site
|
||||||
from .subscribe import Subscribe
|
from .subscribe import Subscribe
|
||||||
from .context import Context
|
from .context import Context, MediaInfo, MetaInfo, TransferTorrent, DownloadingTorrent, TransferInfo, ExistMediaInfo, \
|
||||||
|
NotExistMediaInfo, RefreshMediaItem
|
||||||
from .servarr import RadarrMovie, SonarrSeries
|
from .servarr import RadarrMovie, SonarrSeries
|
||||||
|
@ -80,6 +80,57 @@ class MediaInfo(BaseModel):
|
|||||||
overview: Optional[str] = None
|
overview: Optional[str] = None
|
||||||
# 二级分类
|
# 二级分类
|
||||||
category: str = ""
|
category: str = ""
|
||||||
|
# 季集
|
||||||
|
seasons: Dict[int, list] = {}
|
||||||
|
# 别名和译名
|
||||||
|
names: list = []
|
||||||
|
|
||||||
|
|
||||||
|
class TorrentInfo(BaseModel):
|
||||||
|
# 站点ID
|
||||||
|
site: int = None
|
||||||
|
# 站点名称
|
||||||
|
site_name: Optional[str] = None
|
||||||
|
# 站点Cookie
|
||||||
|
site_cookie: Optional[str] = None
|
||||||
|
# 站点UA
|
||||||
|
site_ua: Optional[str] = None
|
||||||
|
# 站点是否使用代理
|
||||||
|
site_proxy: bool = False
|
||||||
|
# 站点优先级
|
||||||
|
site_order: int = 0
|
||||||
|
# 种子名称
|
||||||
|
title: Optional[str] = None
|
||||||
|
# 种子副标题
|
||||||
|
description: Optional[str] = None
|
||||||
|
# IMDB ID
|
||||||
|
imdbid: str = None
|
||||||
|
# 种子链接
|
||||||
|
enclosure: Optional[str] = None
|
||||||
|
# 详情页面
|
||||||
|
page_url: Optional[str] = None
|
||||||
|
# 种子大小
|
||||||
|
size: float = 0
|
||||||
|
# 做种者
|
||||||
|
seeders: int = 0
|
||||||
|
# 下载者
|
||||||
|
peers: int = 0
|
||||||
|
# 完成者
|
||||||
|
grabs: int = 0
|
||||||
|
# 发布时间
|
||||||
|
pubdate: Optional[str] = None
|
||||||
|
# 已过时间
|
||||||
|
date_elapsed: Optional[str] = None
|
||||||
|
# 上传因子
|
||||||
|
uploadvolumefactor: Optional[float] = None
|
||||||
|
# 下载因子
|
||||||
|
downloadvolumefactor: Optional[float] = None
|
||||||
|
# HR
|
||||||
|
hit_and_run: bool = False
|
||||||
|
# 种子标签
|
||||||
|
labels: Optional[list] = []
|
||||||
|
# 种子优先级
|
||||||
|
pri_order: int = 0
|
||||||
|
|
||||||
|
|
||||||
class Context(BaseModel):
|
class Context(BaseModel):
|
||||||
@ -87,6 +138,8 @@ class Context(BaseModel):
|
|||||||
meta_info: Optional[MetaInfo]
|
meta_info: Optional[MetaInfo]
|
||||||
# 媒体信息
|
# 媒体信息
|
||||||
media_info: Optional[MediaInfo]
|
media_info: Optional[MediaInfo]
|
||||||
|
# 种子信息
|
||||||
|
torrent_info: Optional[TorrentInfo]
|
||||||
|
|
||||||
|
|
||||||
class TransferTorrent(BaseModel):
|
class TransferTorrent(BaseModel):
|
||||||
@ -124,7 +177,7 @@ class ExistMediaInfo(BaseModel):
|
|||||||
# 类型 电影、电视剧
|
# 类型 电影、电视剧
|
||||||
type: MediaType
|
type: MediaType
|
||||||
# 季
|
# 季
|
||||||
seasons: Optional[Dict[int, list]] = None
|
seasons: Dict[int, list] = {}
|
||||||
|
|
||||||
|
|
||||||
class NotExistMediaInfo(BaseModel):
|
class NotExistMediaInfo(BaseModel):
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
from app.chain.douban_sync import DoubanSyncChain
|
from app.chain.douban import DoubanChain
|
||||||
|
|
||||||
|
|
||||||
class DoubanSyncTest(TestCase):
|
class DoubanSyncTest(TestCase):
|
||||||
@ -14,4 +14,4 @@ class DoubanSyncTest(TestCase):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def test_doubansync():
|
def test_doubansync():
|
||||||
DoubanSyncChain().process()
|
DoubanChain().sync()
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
from app.chain.download import DownloadChain
|
from app.chain.download import DownloadChain
|
||||||
from app.chain.identify import IdentifyChain
|
from app.chain.media import MediaChain
|
||||||
from app.core.metainfo import MetaInfo
|
from app.core.metainfo import MetaInfo
|
||||||
|
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ class RecognizeTest(TestCase):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def test_recognize(self):
|
def test_recognize(self):
|
||||||
result = IdentifyChain().process(title="我和我的祖国 2019")
|
result = MediaChain().recognize_by_title(title="我和我的祖国 2019")
|
||||||
self.assertEqual(result.media_info.tmdb_id, 612845)
|
self.assertEqual(result.media_info.tmdb_id, 612845)
|
||||||
exists = DownloadChain().get_no_exists_info(MetaInfo("我和我的祖国 2019"), result.media_info)
|
exists = DownloadChain().get_no_exists_info(MetaInfo("我和我的祖国 2019"), result.media_info)
|
||||||
self.assertTrue(exists[0])
|
self.assertTrue(exists[0])
|
||||||
|
Loading…
x
Reference in New Issue
Block a user