feat 辅助识别异步接口 && ChatGPT插件支持辅助名称识别

This commit is contained in:
jxxghp 2023-10-12 11:41:57 +08:00
parent af2f52a050
commit 2a9a36ac88
22 changed files with 275 additions and 101 deletions

View File

@ -1,14 +1,12 @@
from typing import List, Any from typing import List, Any
from fastapi import APIRouter, Depends, Response from fastapi import APIRouter, Depends, Response
from sqlalchemy.orm import Session
from app import schemas from app import schemas
from app.chain.douban import DoubanChain from app.chain.douban import DoubanChain
from app.core.config import settings from app.core.config import settings
from app.core.context import MediaInfo from app.core.context import MediaInfo
from app.core.security import verify_token from app.core.security import verify_token
from app.db import get_db
from app.schemas import MediaType from app.schemas import MediaType
from app.utils.http import RequestUtils from app.utils.http import RequestUtils
@ -32,13 +30,12 @@ def douban_img(imgurl: str) -> Any:
@router.get("/recognize/{doubanid}", summary="豆瓣ID识别", response_model=schemas.Context) @router.get("/recognize/{doubanid}", summary="豆瓣ID识别", response_model=schemas.Context)
def recognize_doubanid(doubanid: str, def recognize_doubanid(doubanid: str,
db: Session = Depends(get_db),
_: schemas.TokenPayload = Depends(verify_token)) -> Any: _: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
根据豆瓣ID识别媒体信息 根据豆瓣ID识别媒体信息
""" """
# 识别媒体信息 # 识别媒体信息
context = DoubanChain(db).recognize_by_doubanid(doubanid=doubanid) context = DoubanChain().recognize_by_doubanid(doubanid=doubanid)
if context: if context:
return context.to_dict() return context.to_dict()
else: else:
@ -48,12 +45,11 @@ def recognize_doubanid(doubanid: str,
@router.get("/showing", summary="豆瓣正在热映", response_model=List[schemas.MediaInfo]) @router.get("/showing", summary="豆瓣正在热映", response_model=List[schemas.MediaInfo])
def movie_showing(page: int = 1, def movie_showing(page: int = 1,
count: int = 30, count: int = 30,
db: Session = Depends(get_db),
_: schemas.TokenPayload = Depends(verify_token)) -> Any: _: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
浏览豆瓣正在热映 浏览豆瓣正在热映
""" """
movies = DoubanChain(db).movie_showing(page=page, count=count) movies = DoubanChain().movie_showing(page=page, count=count)
if not movies: if not movies:
return [] return []
medias = [MediaInfo(douban_info=movie) for movie in movies] medias = [MediaInfo(douban_info=movie) for movie in movies]
@ -65,12 +61,11 @@ def douban_movies(sort: str = "R",
tags: str = "", tags: str = "",
page: int = 1, page: int = 1,
count: int = 30, count: int = 30,
db: Session = Depends(get_db),
_: schemas.TokenPayload = Depends(verify_token)) -> Any: _: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
浏览豆瓣电影信息 浏览豆瓣电影信息
""" """
movies = DoubanChain(db).douban_discover(mtype=MediaType.MOVIE, movies = DoubanChain().douban_discover(mtype=MediaType.MOVIE,
sort=sort, tags=tags, page=page, count=count) sort=sort, tags=tags, page=page, count=count)
if not movies: if not movies:
return [] return []
@ -86,12 +81,11 @@ def douban_tvs(sort: str = "R",
tags: str = "", tags: str = "",
page: int = 1, page: int = 1,
count: int = 30, count: int = 30,
db: Session = Depends(get_db),
_: schemas.TokenPayload = Depends(verify_token)) -> Any: _: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
浏览豆瓣剧集信息 浏览豆瓣剧集信息
""" """
tvs = DoubanChain(db).douban_discover(mtype=MediaType.TV, tvs = DoubanChain().douban_discover(mtype=MediaType.TV,
sort=sort, tags=tags, page=page, count=count) sort=sort, tags=tags, page=page, count=count)
if not tvs: if not tvs:
return [] return []
@ -106,59 +100,54 @@ def douban_tvs(sort: str = "R",
@router.get("/movie_top250", summary="豆瓣电影TOP250", response_model=List[schemas.MediaInfo]) @router.get("/movie_top250", summary="豆瓣电影TOP250", response_model=List[schemas.MediaInfo])
def movie_top250(page: int = 1, def movie_top250(page: int = 1,
count: int = 30, count: int = 30,
db: Session = Depends(get_db),
_: schemas.TokenPayload = Depends(verify_token)) -> Any: _: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
浏览豆瓣剧集信息 浏览豆瓣剧集信息
""" """
movies = DoubanChain(db).movie_top250(page=page, count=count) movies = DoubanChain().movie_top250(page=page, count=count)
return [MediaInfo(douban_info=movie).to_dict() for movie in movies] return [MediaInfo(douban_info=movie).to_dict() for movie in movies]
@router.get("/tv_weekly_chinese", summary="豆瓣国产剧集周榜", response_model=List[schemas.MediaInfo]) @router.get("/tv_weekly_chinese", summary="豆瓣国产剧集周榜", response_model=List[schemas.MediaInfo])
def tv_weekly_chinese(page: int = 1, def tv_weekly_chinese(page: int = 1,
count: int = 30, count: int = 30,
db: Session = Depends(get_db),
_: schemas.TokenPayload = Depends(verify_token)) -> Any: _: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
中国每周剧集口碑榜 中国每周剧集口碑榜
""" """
tvs = DoubanChain(db).tv_weekly_chinese(page=page, count=count) tvs = DoubanChain().tv_weekly_chinese(page=page, count=count)
return [MediaInfo(douban_info=tv).to_dict() for tv in tvs] return [MediaInfo(douban_info=tv).to_dict() for tv in tvs]
@router.get("/tv_weekly_global", summary="豆瓣全球剧集周榜", response_model=List[schemas.MediaInfo]) @router.get("/tv_weekly_global", summary="豆瓣全球剧集周榜", response_model=List[schemas.MediaInfo])
def tv_weekly_global(page: int = 1, def tv_weekly_global(page: int = 1,
count: int = 30, count: int = 30,
db: Session = Depends(get_db),
_: schemas.TokenPayload = Depends(verify_token)) -> Any: _: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
全球每周剧集口碑榜 全球每周剧集口碑榜
""" """
tvs = DoubanChain(db).tv_weekly_global(page=page, count=count) tvs = DoubanChain().tv_weekly_global(page=page, count=count)
return [MediaInfo(douban_info=tv).to_dict() for tv in tvs] return [MediaInfo(douban_info=tv).to_dict() for tv in tvs]
@router.get("/tv_animation", summary="豆瓣动画剧集", response_model=List[schemas.MediaInfo]) @router.get("/tv_animation", summary="豆瓣动画剧集", response_model=List[schemas.MediaInfo])
def tv_animation(page: int = 1, def tv_animation(page: int = 1,
count: int = 30, count: int = 30,
db: Session = Depends(get_db),
_: schemas.TokenPayload = Depends(verify_token)) -> Any: _: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
热门动画剧集 热门动画剧集
""" """
tvs = DoubanChain(db).tv_animation(page=page, count=count) tvs = DoubanChain().tv_animation(page=page, count=count)
return [MediaInfo(douban_info=tv).to_dict() for tv in tvs] return [MediaInfo(douban_info=tv).to_dict() for tv in tvs]
@router.get("/{doubanid}", summary="查询豆瓣详情", response_model=schemas.MediaInfo) @router.get("/{doubanid}", summary="查询豆瓣详情", response_model=schemas.MediaInfo)
def douban_info(doubanid: str, def douban_info(doubanid: str,
db: Session = Depends(get_db),
_: schemas.TokenPayload = Depends(verify_token)) -> Any: _: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
根据豆瓣ID查询豆瓣媒体信息 根据豆瓣ID查询豆瓣媒体信息
""" """
doubaninfo = DoubanChain(db).douban_info(doubanid=doubanid) doubaninfo = DoubanChain().douban_info(doubanid=doubanid)
if doubaninfo: if doubaninfo:
return MediaInfo(douban_info=doubaninfo).to_dict() return MediaInfo(douban_info=doubaninfo).to_dict()
else: else:

View File

@ -68,12 +68,12 @@ def exists(media_in: schemas.MediaInfo,
if media_in.tmdb_id: if media_in.tmdb_id:
mediainfo.from_dict(media_in.dict()) mediainfo.from_dict(media_in.dict())
elif media_in.douban_id: elif media_in.douban_id:
context = DoubanChain(db).recognize_by_doubanid(doubanid=media_in.douban_id) context = DoubanChain().recognize_by_doubanid(doubanid=media_in.douban_id)
if context: if context:
mediainfo = context.media_info mediainfo = context.media_info
meta = context.meta_info meta = context.meta_info
else: else:
context = MediaChain(db).recognize_by_title(title=f"{media_in.title} {media_in.year}") context = MediaChain().recognize_by_title(title=f"{media_in.title} {media_in.year}")
if context: if context:
mediainfo = context.media_info mediainfo = context.media_info
meta = context.meta_info meta = context.meta_info

View File

@ -74,11 +74,11 @@ def bing_wallpaper() -> Any:
@router.get("/tmdb", summary="TMDB电影海报", response_model=schemas.Response) @router.get("/tmdb", summary="TMDB电影海报", response_model=schemas.Response)
def tmdb_wallpaper(db: Session = Depends(get_db)) -> Any: def tmdb_wallpaper() -> Any:
""" """
获取TMDB电影海报 获取TMDB电影海报
""" """
wallpager = TmdbChain(db).get_random_wallpager() wallpager = TmdbChain().get_random_wallpager()
if wallpager: if wallpager:
return schemas.Response( return schemas.Response(
success=True, success=True,

View File

@ -20,13 +20,12 @@ router = APIRouter()
@router.get("/recognize", summary="识别媒体信息(种子)", response_model=schemas.Context) @router.get("/recognize", summary="识别媒体信息(种子)", response_model=schemas.Context)
def recognize(title: str, def recognize(title: str,
subtitle: str = None, subtitle: str = None,
db: Session = Depends(get_db),
_: schemas.TokenPayload = Depends(verify_token)) -> Any: _: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
根据标题副标题识别媒体信息 根据标题副标题识别媒体信息
""" """
# 识别媒体信息 # 识别媒体信息
context = MediaChain(db).recognize_by_title(title=title, subtitle=subtitle) context = MediaChain().recognize_by_title(title=title, subtitle=subtitle)
if context: if context:
return context.to_dict() return context.to_dict()
return schemas.Context() return schemas.Context()
@ -34,13 +33,12 @@ def recognize(title: str,
@router.get("/recognize_file", summary="识别媒体信息(文件)", response_model=schemas.Context) @router.get("/recognize_file", summary="识别媒体信息(文件)", response_model=schemas.Context)
def recognize(path: str, def recognize(path: str,
db: Session = Depends(get_db),
_: schemas.TokenPayload = Depends(verify_token)) -> Any: _: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
根据文件路径识别媒体信息 根据文件路径识别媒体信息
""" """
# 识别媒体信息 # 识别媒体信息
context = MediaChain(db).recognize_by_path(path) context = MediaChain().recognize_by_path(path)
if context: if context:
return context.to_dict() return context.to_dict()
return schemas.Context() return schemas.Context()
@ -50,12 +48,11 @@ def recognize(path: str,
def search_by_title(title: str, def search_by_title(title: str,
page: int = 1, page: int = 1,
count: int = 8, count: int = 8,
db: Session = Depends(get_db),
_: schemas.TokenPayload = Depends(verify_token)) -> Any: _: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
模糊搜索媒体信息列表 模糊搜索媒体信息列表
""" """
_, medias = MediaChain(db).search(title=title) _, medias = MediaChain().search(title=title)
if medias: if medias:
return [media.to_dict() for media in medias[(page - 1) * count: page * count]] return [media.to_dict() for media in medias[(page - 1) * count: page * count]]
return [] return []
@ -85,21 +82,20 @@ def exists(title: str = None,
@router.get("/{mediaid}", summary="查询媒体详情", response_model=schemas.MediaInfo) @router.get("/{mediaid}", summary="查询媒体详情", response_model=schemas.MediaInfo)
def tmdb_info(mediaid: str, type_name: str, def tmdb_info(mediaid: str, type_name: str,
db: Session = Depends(get_db),
_: schemas.TokenPayload = Depends(verify_token)) -> Any: _: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
根据媒体ID查询themoviedb或豆瓣媒体信息type_name: 电影/电视剧 根据媒体ID查询themoviedb或豆瓣媒体信息type_name: 电影/电视剧
""" """
mtype = MediaType(type_name) mtype = MediaType(type_name)
if mediaid.startswith("tmdb:"): if mediaid.startswith("tmdb:"):
result = TmdbChain(db).tmdb_info(int(mediaid[5:]), mtype) result = TmdbChain().tmdb_info(int(mediaid[5:]), mtype)
return MediaInfo(tmdb_info=result).to_dict() return MediaInfo(tmdb_info=result).to_dict()
elif mediaid.startswith("douban:"): elif mediaid.startswith("douban:"):
# 查询豆瓣信息 # 查询豆瓣信息
doubaninfo = DoubanChain(db).douban_info(doubanid=mediaid[7:]) doubaninfo = DoubanChain().douban_info(doubanid=mediaid[7:])
if not doubaninfo: if not doubaninfo:
return schemas.MediaInfo() return schemas.MediaInfo()
result = DoubanChain(db).recognize_by_doubaninfo(doubaninfo) result = DoubanChain().recognize_by_doubaninfo(doubaninfo)
if result: if result:
# TMDB # TMDB
return result.media_info.to_dict() return result.media_info.to_dict()

View File

@ -40,7 +40,7 @@ def search_by_tmdbid(mediaid: str,
elif mediaid.startswith("douban:"): elif mediaid.startswith("douban:"):
doubanid = mediaid.replace("douban:", "") doubanid = mediaid.replace("douban:", "")
# 识别豆瓣信息 # 识别豆瓣信息
context = DoubanChain(db).recognize_by_doubanid(doubanid) context = DoubanChain().recognize_by_doubanid(doubanid)
if not context or not context.media_info or not context.media_info.tmdb_id: if not context or not context.media_info or not context.media_info.tmdb_id:
return [] return []
torrents = SearchChain(db).search_by_tmdbid(tmdbid=context.media_info.tmdb_id, torrents = SearchChain(db).search_by_tmdbid(tmdbid=context.media_info.tmdb_id,

View File

@ -223,7 +223,7 @@ def execute_command(jobid: str,
if not jobid: if not jobid:
return schemas.Response(success=False, message="命令不能为空!") return schemas.Response(success=False, message="命令不能为空!")
if jobid == "subscribe_search": if jobid == "subscribe_search":
Scheduler().start(jobid, state = 'R') Scheduler().start(jobid, state='R')
else: else:
Scheduler().start(jobid) Scheduler().start(jobid)
return schemas.Response(success=True) return schemas.Response(success=True)

View File

@ -1,25 +1,22 @@
from typing import List, Any from typing import List, Any
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from app import schemas from app import schemas
from app.chain.tmdb import TmdbChain from app.chain.tmdb import TmdbChain
from app.core.context import MediaInfo from app.core.context import MediaInfo
from app.core.security import verify_token from app.core.security import verify_token
from app.db import get_db
from app.schemas.types import MediaType from app.schemas.types import MediaType
router = APIRouter() router = APIRouter()
@router.get("/seasons/{tmdbid}", summary="TMDB所有季", response_model=List[schemas.TmdbSeason]) @router.get("/seasons/{tmdbid}", summary="TMDB所有季", response_model=List[schemas.TmdbSeason])
def tmdb_seasons(tmdbid: int, db: Session = Depends(get_db), def tmdb_seasons(tmdbid: int, _: schemas.TokenPayload = Depends(verify_token)) -> Any:
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
根据TMDBID查询themoviedb所有季信息 根据TMDBID查询themoviedb所有季信息
""" """
seasons_info = TmdbChain(db).tmdb_seasons(tmdbid=tmdbid) seasons_info = TmdbChain().tmdb_seasons(tmdbid=tmdbid)
if not seasons_info: if not seasons_info:
return [] return []
else: else:
@ -29,16 +26,15 @@ def tmdb_seasons(tmdbid: int, db: Session = Depends(get_db),
@router.get("/similar/{tmdbid}/{type_name}", summary="类似电影/电视剧", response_model=List[schemas.MediaInfo]) @router.get("/similar/{tmdbid}/{type_name}", summary="类似电影/电视剧", response_model=List[schemas.MediaInfo])
def tmdb_similar(tmdbid: int, def tmdb_similar(tmdbid: int,
type_name: str, type_name: str,
db: Session = Depends(get_db),
_: schemas.TokenPayload = Depends(verify_token)) -> Any: _: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
根据TMDBID查询类似电影/电视剧type_name: 电影/电视剧 根据TMDBID查询类似电影/电视剧type_name: 电影/电视剧
""" """
mediatype = MediaType(type_name) mediatype = MediaType(type_name)
if mediatype == MediaType.MOVIE: if mediatype == MediaType.MOVIE:
tmdbinfos = TmdbChain(db).movie_similar(tmdbid=tmdbid) tmdbinfos = TmdbChain().movie_similar(tmdbid=tmdbid)
elif mediatype == MediaType.TV: elif mediatype == MediaType.TV:
tmdbinfos = TmdbChain(db).tv_similar(tmdbid=tmdbid) tmdbinfos = TmdbChain().tv_similar(tmdbid=tmdbid)
else: else:
return [] return []
if not tmdbinfos: if not tmdbinfos:
@ -50,16 +46,15 @@ def tmdb_similar(tmdbid: int,
@router.get("/recommend/{tmdbid}/{type_name}", summary="推荐电影/电视剧", response_model=List[schemas.MediaInfo]) @router.get("/recommend/{tmdbid}/{type_name}", summary="推荐电影/电视剧", response_model=List[schemas.MediaInfo])
def tmdb_recommend(tmdbid: int, def tmdb_recommend(tmdbid: int,
type_name: str, type_name: str,
db: Session = Depends(get_db),
_: schemas.TokenPayload = Depends(verify_token)) -> Any: _: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
根据TMDBID查询推荐电影/电视剧type_name: 电影/电视剧 根据TMDBID查询推荐电影/电视剧type_name: 电影/电视剧
""" """
mediatype = MediaType(type_name) mediatype = MediaType(type_name)
if mediatype == MediaType.MOVIE: if mediatype == MediaType.MOVIE:
tmdbinfos = TmdbChain(db).movie_recommend(tmdbid=tmdbid) tmdbinfos = TmdbChain().movie_recommend(tmdbid=tmdbid)
elif mediatype == MediaType.TV: elif mediatype == MediaType.TV:
tmdbinfos = TmdbChain(db).tv_recommend(tmdbid=tmdbid) tmdbinfos = TmdbChain().tv_recommend(tmdbid=tmdbid)
else: else:
return [] return []
if not tmdbinfos: if not tmdbinfos:
@ -72,16 +67,15 @@ def tmdb_recommend(tmdbid: int,
def tmdb_credits(tmdbid: int, def tmdb_credits(tmdbid: int,
type_name: str, type_name: str,
page: int = 1, page: int = 1,
db: Session = Depends(get_db),
_: schemas.TokenPayload = Depends(verify_token)) -> Any: _: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
根据TMDBID查询演员阵容type_name: 电影/电视剧 根据TMDBID查询演员阵容type_name: 电影/电视剧
""" """
mediatype = MediaType(type_name) mediatype = MediaType(type_name)
if mediatype == MediaType.MOVIE: if mediatype == MediaType.MOVIE:
tmdbinfos = TmdbChain(db).movie_credits(tmdbid=tmdbid, page=page) tmdbinfos = TmdbChain().movie_credits(tmdbid=tmdbid, page=page)
elif mediatype == MediaType.TV: elif mediatype == MediaType.TV:
tmdbinfos = TmdbChain(db).tv_credits(tmdbid=tmdbid, page=page) tmdbinfos = TmdbChain().tv_credits(tmdbid=tmdbid, page=page)
else: else:
return [] return []
if not tmdbinfos: if not tmdbinfos:
@ -92,12 +86,11 @@ def tmdb_credits(tmdbid: int,
@router.get("/person/{person_id}", summary="人物详情", response_model=schemas.TmdbPerson) @router.get("/person/{person_id}", summary="人物详情", response_model=schemas.TmdbPerson)
def tmdb_person(person_id: int, def tmdb_person(person_id: int,
db: Session = Depends(get_db),
_: schemas.TokenPayload = Depends(verify_token)) -> Any: _: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
根据人物ID查询人物详情 根据人物ID查询人物详情
""" """
tmdbinfo = TmdbChain(db).person_detail(person_id=person_id) tmdbinfo = TmdbChain().person_detail(person_id=person_id)
if not tmdbinfo: if not tmdbinfo:
return schemas.TmdbPerson() return schemas.TmdbPerson()
else: else:
@ -107,12 +100,11 @@ def tmdb_person(person_id: int,
@router.get("/person/credits/{person_id}", summary="人物参演作品", response_model=List[schemas.MediaInfo]) @router.get("/person/credits/{person_id}", summary="人物参演作品", response_model=List[schemas.MediaInfo])
def tmdb_person_credits(person_id: int, def tmdb_person_credits(person_id: int,
page: int = 1, page: int = 1,
db: Session = Depends(get_db),
_: schemas.TokenPayload = Depends(verify_token)) -> Any: _: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
根据人物ID查询人物参演作品 根据人物ID查询人物参演作品
""" """
tmdbinfo = TmdbChain(db).person_credits(person_id=person_id, page=page) tmdbinfo = TmdbChain().person_credits(person_id=person_id, page=page)
if not tmdbinfo: if not tmdbinfo:
return [] return []
else: else:
@ -124,12 +116,11 @@ def tmdb_movies(sort_by: str = "popularity.desc",
with_genres: str = "", with_genres: str = "",
with_original_language: str = "", with_original_language: str = "",
page: int = 1, page: int = 1,
db: Session = Depends(get_db),
_: schemas.TokenPayload = Depends(verify_token)) -> Any: _: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
浏览TMDB电影信息 浏览TMDB电影信息
""" """
movies = TmdbChain(db).tmdb_discover(mtype=MediaType.MOVIE, movies = TmdbChain().tmdb_discover(mtype=MediaType.MOVIE,
sort_by=sort_by, sort_by=sort_by,
with_genres=with_genres, with_genres=with_genres,
with_original_language=with_original_language, with_original_language=with_original_language,
@ -144,12 +135,11 @@ def tmdb_tvs(sort_by: str = "popularity.desc",
with_genres: str = "", with_genres: str = "",
with_original_language: str = "", with_original_language: str = "",
page: int = 1, page: int = 1,
db: Session = Depends(get_db),
_: schemas.TokenPayload = Depends(verify_token)) -> Any: _: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
浏览TMDB剧集信息 浏览TMDB剧集信息
""" """
tvs = TmdbChain(db).tmdb_discover(mtype=MediaType.TV, tvs = TmdbChain().tmdb_discover(mtype=MediaType.TV,
sort_by=sort_by, sort_by=sort_by,
with_genres=with_genres, with_genres=with_genres,
with_original_language=with_original_language, with_original_language=with_original_language,
@ -161,12 +151,11 @@ def tmdb_tvs(sort_by: str = "popularity.desc",
@router.get("/trending", summary="TMDB流行趋势", response_model=List[schemas.MediaInfo]) @router.get("/trending", summary="TMDB流行趋势", response_model=List[schemas.MediaInfo])
def tmdb_trending(page: int = 1, def tmdb_trending(page: int = 1,
db: Session = Depends(get_db),
_: schemas.TokenPayload = Depends(verify_token)) -> Any: _: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
浏览TMDB剧集信息 浏览TMDB剧集信息
""" """
infos = TmdbChain(db).tmdb_trending(page=page) infos = TmdbChain().tmdb_trending(page=page)
if not infos: if not infos:
return [] return []
return [MediaInfo(tmdb_info=info).to_dict() for info in infos] return [MediaInfo(tmdb_info=info).to_dict() for info in infos]
@ -174,12 +163,11 @@ def tmdb_trending(page: int = 1,
@router.get("/{tmdbid}/{season}", summary="TMDB季所有集", response_model=List[schemas.TmdbEpisode]) @router.get("/{tmdbid}/{season}", summary="TMDB季所有集", response_model=List[schemas.TmdbEpisode])
def tmdb_season_episodes(tmdbid: int, season: int, def tmdb_season_episodes(tmdbid: int, season: int,
db: Session = Depends(get_db),
_: schemas.TokenPayload = Depends(verify_token)) -> Any: _: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
根据TMDBID查询某季的所有信信息 根据TMDBID查询某季的所有信信息
""" """
episodes_info = TmdbChain(db).tmdb_episodes(tmdbid=tmdbid, season=season) episodes_info = TmdbChain().tmdb_episodes(tmdbid=tmdbid, season=season)
if not episodes_info: if not episodes_info:
return [] return []
else: else:

View File

@ -301,11 +301,11 @@ def arr_movie_lookup(apikey: str, term: str, db: Session = Depends(get_db)) -> A
) )
tmdbid = term.replace("tmdb:", "") tmdbid = term.replace("tmdb:", "")
# 查询媒体信息 # 查询媒体信息
mediainfo = MediaChain(db).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 = MediaChain(db).media_exists(mediainfo=mediainfo) exists = MediaChain().media_exists(mediainfo=mediainfo)
if not exists: if not exists:
# 文件不存在 # 文件不存在
hasfile = False hasfile = False
@ -581,7 +581,7 @@ def arr_series_lookup(apikey: str, term: str, db: Session = Depends(get_db)) ->
# 获取TVDBID # 获取TVDBID
if not term.startswith("tvdb:"): if not term.startswith("tvdb:"):
mediainfo = MediaChain(db).recognize_media(meta=MetaInfo(term), mediainfo = MediaChain().recognize_media(meta=MetaInfo(term),
mtype=MediaType.TV) mtype=MediaType.TV)
if not mediainfo: if not mediainfo:
return [SonarrSeries()] return [SonarrSeries()]
@ -593,7 +593,7 @@ def arr_series_lookup(apikey: str, term: str, db: Session = Depends(get_db)) ->
tvdbid = int(term.replace("tvdb:", "")) tvdbid = int(term.replace("tvdb:", ""))
# 查询TVDB信息 # 查询TVDB信息
tvdbinfo = MediaChain(db).tvdb_info(tvdbid=tvdbid) tvdbinfo = MediaChain().tvdb_info(tvdbid=tvdbid)
if not tvdbinfo: if not tvdbinfo:
return [SonarrSeries()] return [SonarrSeries()]
@ -605,11 +605,11 @@ def arr_series_lookup(apikey: str, term: str, db: Session = Depends(get_db)) ->
# 根据TVDB查询媒体信息 # 根据TVDB查询媒体信息
if not mediainfo: if not mediainfo:
mediainfo = MediaChain(db).recognize_media(meta=MetaInfo(tvdbinfo.get('seriesName')), mediainfo = MediaChain().recognize_media(meta=MetaInfo(tvdbinfo.get('seriesName')),
mtype=MediaType.TV) mtype=MediaType.TV)
# 查询是否存在 # 查询是否存在
exists = MediaChain(db).media_exists(mediainfo) exists = MediaChain().media_exists(mediainfo)
if exists: if exists:
hasfile = True hasfile = True
else: else:

View File

@ -6,11 +6,12 @@ from app.core.context import MediaInfo
from app.core.metainfo import MetaInfo from app.core.metainfo import MetaInfo
from app.log import logger from app.log import logger
from app.schemas import MediaType from app.schemas import MediaType
from app.utils.singleton import Singleton
class DoubanChain(ChainBase): class DoubanChain(ChainBase, metaclass=Singleton):
""" """
豆瓣处理链 豆瓣处理链单例运行
""" """
def recognize_by_doubanid(self, doubanid: str) -> Optional[Context]: def recognize_by_doubanid(self, doubanid: str) -> Optional[Context]:

View File

@ -1,18 +1,31 @@
import copy
import time
from pathlib import Path from pathlib import Path
from threading import Lock
from typing import Optional, List, Tuple from typing import Optional, List, Tuple
from app.chain import ChainBase from app.chain import ChainBase
from app.core.context import Context, MediaInfo from app.core.context import Context, MediaInfo
from app.core.event import eventmanager, Event
from app.core.meta import MetaBase from app.core.meta import MetaBase
from app.core.metainfo import MetaInfo, MetaInfoPath from app.core.metainfo import MetaInfo, MetaInfoPath
from app.log import logger from app.log import logger
from app.schemas.types import EventType, MediaType
from app.utils.singleton import Singleton
from app.utils.string import StringUtils from app.utils.string import StringUtils
class MediaChain(ChainBase): recognize_lock = Lock()
class MediaChain(ChainBase, metaclass=Singleton):
""" """
媒体信息处理链 媒体信息处理链单例运行
""" """
# 临时识别标题
recognize_title: Optional[str] = None
# 临时识别结果 {title, name, year, season, episode}
recognize_temp: Optional[dict] = None
def recognize_by_title(self, title: str, subtitle: str = None) -> Optional[Context]: def recognize_by_title(self, title: str, subtitle: str = None) -> Optional[Context]:
""" """
@ -23,15 +36,105 @@ class MediaChain(ChainBase):
metainfo = MetaInfo(title, subtitle) metainfo = MetaInfo(title, subtitle)
# 识别媒体信息 # 识别媒体信息
mediainfo: MediaInfo = self.recognize_media(meta=metainfo) mediainfo: MediaInfo = self.recognize_media(meta=metainfo)
if not mediainfo:
# 偿试使用辅助识别,如果有注册响应事件的话
if eventmanager.check(EventType.NameRecognize):
logger.info(f'请求辅助识别,标题:{title} ...')
mediainfo = self.recognize_help(title=title, org_meta=metainfo)
if not mediainfo: if not mediainfo:
logger.warn(f'{title} 未识别到媒体信息') logger.warn(f'{title} 未识别到媒体信息')
return Context(meta_info=metainfo) return Context(meta_info=metainfo)
# 识别成功
logger.info(f'{title} 识别到媒体信息:{mediainfo.type.value} {mediainfo.title_year}') logger.info(f'{title} 识别到媒体信息:{mediainfo.type.value} {mediainfo.title_year}')
# 更新媒体图片 # 更新媒体图片
self.obtain_images(mediainfo=mediainfo) self.obtain_images(mediainfo=mediainfo)
# 返回上下文 # 返回上下文
return Context(meta_info=metainfo, media_info=mediainfo) return Context(meta_info=metainfo, media_info=mediainfo)
def recognize_help(self, title: str, org_meta: MetaBase) -> Optional[MediaInfo]:
"""
请求辅助识别返回媒体信息
:param title: 标题
:param org_meta: 原始元数据
"""
with recognize_lock:
self.recognize_temp = None
self.recognize_title = title
# 发送请求事件
eventmanager.send_event(
EventType.NameRecognize,
{
'title': title,
}
)
# 每0.5秒循环一次等待结果直到10秒后超时
for i in range(10):
if self.recognize_temp is not None:
break
time.sleep(0.5)
# 加锁
with recognize_lock:
mediainfo = None
if not self.recognize_temp or self.recognize_title != title:
# 没有识别结果或者识别标题已改变
return None
# 有识别结果
meta_dict = copy.deepcopy(self.recognize_temp)
logger.info(f'获取到辅助识别结果:{meta_dict}')
if meta_dict.get("name") == org_meta.name and meta_dict.get("year") == org_meta.year:
logger.info(f'辅助识别结果与原始识别结果一致')
else:
logger.info(f'辅助识别结果与原始识别结果不一致,重新匹配媒体信息 ...')
org_meta.name = meta_dict.get("name")
org_meta.year = meta_dict.get("year")
org_meta.begin_season = meta_dict.get("season")
org_meta.begin_episode = meta_dict.get("episode")
if org_meta.begin_season or org_meta.begin_episode:
org_meta.type = MediaType.TV
# 重新识别
mediainfo = self.recognize_media(meta=org_meta)
return mediainfo
@eventmanager.register(EventType.NameRecognizeResult)
def recognize_result(self, event: Event):
"""
监控识别结果事件获取辅助识别结果结果格式{title, name, year, season, episode}
"""
if not event:
return
event_data = event.event_data or {}
# 加锁
with recognize_lock:
# 不是原标题的结果不要
if event_data.get("title") != self.recognize_title:
return
# 标志收到返回
self.recognize_temp = {}
# 处理数据格式
file_title, file_year, season_number, episode_number = None, None, None, None
if event_data.get("name"):
file_title = str(event_data["name"]).split("/")[0].strip().replace(".", " ")
if event_data.get("year"):
file_year = str(event_data["year"]).split("/")[0].strip()
if event_data.get("season") and str(event_data["season"]).isdigit():
season_number = int(event_data["season"])
if event_data.get("episode") and str(event_data["episode"]).isdigit():
episode_number = int(event_data["episode"])
if not file_title:
return
if file_title == 'Unknown':
return
if not str(file_year).isdigit():
file_year = None
# 结果赋值
self.recognize_temp = {
"name": file_title,
"year": file_year,
"season": season_number,
"episode": episode_number
}
def recognize_by_path(self, path: str) -> Optional[Context]: def recognize_by_path(self, path: str) -> Optional[Context]:
""" """
根据文件路径识别媒体信息 根据文件路径识别媒体信息
@ -42,6 +145,11 @@ class MediaChain(ChainBase):
file_meta = MetaInfoPath(file_path) file_meta = MetaInfoPath(file_path)
# 识别媒体信息 # 识别媒体信息
mediainfo = self.recognize_media(meta=file_meta) mediainfo = self.recognize_media(meta=file_meta)
if not mediainfo:
# 偿试使用辅助识别,如果有注册响应事件的话
if eventmanager.check(EventType.NameRecognize):
logger.info(f'请求辅助识别,标题:{file_path.name} ...')
mediainfo = self.recognize_help(title=path, org_meta=file_meta)
if not mediainfo: if not mediainfo:
logger.warn(f'{path} 未识别到媒体信息') logger.warn(f'{path} 未识别到媒体信息')
return Context(meta_info=file_meta) return Context(meta_info=file_meta)

View File

@ -32,7 +32,7 @@ class MessageChain(ChainBase):
self.downloadchain = DownloadChain(self._db) self.downloadchain = DownloadChain(self._db)
self.subscribechain = SubscribeChain(self._db) self.subscribechain = SubscribeChain(self._db)
self.searchchain = SearchChain(self._db) self.searchchain = SearchChain(self._db)
self.medtachain = MediaChain(self._db) self.medtachain = MediaChain()
self.torrent = TorrentHelper() self.torrent = TorrentHelper()
self.eventmanager = EventManager() self.eventmanager = EventManager()
self.torrenthelper = TorrentHelper() self.torrenthelper = TorrentHelper()

View File

@ -12,7 +12,7 @@ from app.utils.singleton import Singleton
class TmdbChain(ChainBase, metaclass=Singleton): class TmdbChain(ChainBase, metaclass=Singleton):
""" """
TheMovieDB处理链 TheMovieDB处理链单例运行
""" """
def tmdb_discover(self, mtype: MediaType, sort_by: str, with_genres: str, def tmdb_discover(self, mtype: MediaType, sort_by: str, with_genres: str,

View File

@ -41,8 +41,8 @@ class TransferChain(ChainBase):
self.downloadhis = DownloadHistoryOper(self._db) self.downloadhis = DownloadHistoryOper(self._db)
self.transferhis = TransferHistoryOper(self._db) self.transferhis = TransferHistoryOper(self._db)
self.progress = ProgressHelper() self.progress = ProgressHelper()
self.mediachain = MediaChain(self._db) self.mediachain = MediaChain()
self.tmdbchain = TmdbChain(self._db) self.tmdbchain = TmdbChain()
self.systemconfig = SystemConfigOper() self.systemconfig = SystemConfigOper()
def process(self) -> bool: def process(self) -> bool:

View File

@ -1,3 +1,4 @@
import importlib
import traceback import traceback
from threading import Thread, Event from threading import Thread, Event
from typing import Any, Union, Dict from typing import Any, Union, Dict
@ -175,10 +176,24 @@ class Command(metaclass=Singleton):
for handler in handlers: for handler in handlers:
try: try:
names = handler.__qualname__.split(".") names = handler.__qualname__.split(".")
if names[0] == "Command": [class_name, method_name] = names
self.command_event(event) if class_name in self.pluginmanager.get_plugin_ids():
# 插件事件
self.pluginmanager.run_plugin_method(class_name, method_name, event)
else: else:
self.pluginmanager.run_plugin_method(names[0], names[1], event) # 检查全局变量中是否存在
if class_name not in globals():
# 导入模块除了插件和Command本身只有chain能响应事件
module = importlib.import_module(
f"app.chain.{class_name[:-5].lower()}"
)
class_obj = getattr(module, class_name)()
else:
# 通过类名创建类实例
class_obj = globals()[class_name]()
# 检查类是否存在并调用方法
if hasattr(class_obj, method_name):
getattr(class_obj, method_name)(event)
except Exception as e: except Exception as e:
logger.error(f"事件处理出错:{str(e)} - {traceback.format_exc()}") logger.error(f"事件处理出错:{str(e)} - {traceback.format_exc()}")

View File

@ -32,6 +32,12 @@ class EventManager(metaclass=Singleton):
except Empty: except Empty:
return None, [] return None, []
def check(self, etype: EventType):
"""
检查事件是否存在响应
"""
return etype.value in self._handlers
def add_event_listener(self, etype: EventType, handler: type): def add_event_listener(self, etype: EventType, handler: type):
""" """
注册事件处理 注册事件处理

View File

@ -87,6 +87,17 @@ class MetaBase(object):
return self.cn_name return self.cn_name
return "" return ""
@name.setter
def name(self, name: str):
"""
设置名称
"""
if StringUtils.is_all_chinese(name):
self.cn_name = name
else:
self.en_name = name
self.cn_name = None
def init_subtitle(self, title_text: str): def init_subtitle(self, title_text: str):
""" """
副标题识别 副标题识别

View File

@ -177,6 +177,12 @@ class PluginManager(metaclass=Singleton):
return None return None
return getattr(self._running_plugins[pid], method)(*args, **kwargs) return getattr(self._running_plugins[pid], method)(*args, **kwargs)
def get_plugin_ids(self) -> List[str]:
"""
获取所有插件ID
"""
return list(self._plugins.keys())
def get_plugin_apps(self) -> List[dict]: def get_plugin_apps(self) -> List[dict]:
""" """
获取所有插件信息 获取所有插件信息

View File

@ -1,7 +1,8 @@
from typing import Any, List, Dict, Tuple from typing import Any, List, Dict, Tuple
from app.core.config import settings from app.core.config import settings
from app.core.event import eventmanager from app.core.event import eventmanager, Event
from app.log import logger
from app.plugins import _PluginBase from app.plugins import _PluginBase
from app.plugins.chatgpt.openai import OpenAi from app.plugins.chatgpt.openai import OpenAi
from app.schemas.types import EventType from app.schemas.types import EventType
@ -33,6 +34,7 @@ class ChatGPT(_PluginBase):
openai = None openai = None
_enabled = False _enabled = False
_proxy = False _proxy = False
_recognize = False
_openai_url = None _openai_url = None
_openai_key = None _openai_key = None
@ -40,6 +42,7 @@ class ChatGPT(_PluginBase):
if config: if config:
self._enabled = config.get("enabled") self._enabled = config.get("enabled")
self._proxy = config.get("proxy") self._proxy = config.get("proxy")
self._recognize = config.get("recognize")
self._openai_url = config.get("openai_url") self._openai_url = config.get("openai_url")
self._openai_key = config.get("openai_key") self._openai_key = config.get("openai_key")
self.openai = OpenAi(api_key=self._openai_key, api_url=self._openai_url, self.openai = OpenAi(api_key=self._openai_key, api_url=self._openai_url,
@ -70,7 +73,7 @@ class ChatGPT(_PluginBase):
'component': 'VCol', 'component': 'VCol',
'props': { 'props': {
'cols': 12, 'cols': 12,
'md': 6 'md': 4
}, },
'content': [ 'content': [
{ {
@ -86,7 +89,7 @@ class ChatGPT(_PluginBase):
'component': 'VCol', 'component': 'VCol',
'props': { 'props': {
'cols': 12, 'cols': 12,
'md': 6 'md': 4
}, },
'content': [ 'content': [
{ {
@ -97,6 +100,22 @@ class ChatGPT(_PluginBase):
} }
} }
] ]
},
{
'component': 'VCol',
'props': {
'cols': 12,
'md': 4
},
'content': [
{
'component': 'VSwitch',
'props': {
'model': 'recognize',
'label': '辅助识别',
}
}
]
} }
] ]
}, },
@ -143,6 +162,7 @@ class ChatGPT(_PluginBase):
], { ], {
"enabled": False, "enabled": False,
"proxy": False, "proxy": False,
"recognize": False,
"openai_url": "https://api.openai.com", "openai_url": "https://api.openai.com",
"openai_key": "" "openai_key": ""
} }
@ -151,10 +171,12 @@ class ChatGPT(_PluginBase):
pass pass
@eventmanager.register(EventType.UserMessage) @eventmanager.register(EventType.UserMessage)
def talk(self, event): def talk(self, event: Event):
""" """
监听用户消息获取ChatGPT回复 监听用户消息获取ChatGPT回复
""" """
if not self._enabled:
return
if not self.openai: if not self.openai:
return return
text = event.event_data.get("text") text = event.event_data.get("text")
@ -166,6 +188,34 @@ class ChatGPT(_PluginBase):
if response: if response:
self.post_message(channel=channel, title=response, userid=userid) self.post_message(channel=channel, title=response, userid=userid)
@eventmanager.register(EventType.NameRecognize)
def recognize(self, event: Event):
"""
监听识别事件使用ChatGPT辅助识别名称
"""
if not self._enabled:
return
if not self.openai:
return
if not event.event_data:
return
title = event.event_data.get("title")
if not title:
return
response = self.openai.get_media_name(filename=title)
logger.info(f"ChatGPT辅助识别结果{response}")
if response:
eventmanager.send_event(
EventType.NameRecognizeResult,
{
'title': title,
'name': response.get("title"),
'year': response.get("year"),
'season': response.get("season"),
'episode': response.get("episode")
}
)
def stop_service(self): def stop_service(self):
""" """
退出插件 退出插件

View File

@ -97,7 +97,7 @@ class DirMonitor(_PluginBase):
self.transferhis = TransferHistoryOper(self.db) self.transferhis = TransferHistoryOper(self.db)
self.downloadhis = DownloadHistoryOper(self.db) self.downloadhis = DownloadHistoryOper(self.db)
self.transferchian = TransferChain(self.db) self.transferchian = TransferChain(self.db)
self.tmdbchain = TmdbChain(self.db) self.tmdbchain = TmdbChain()
# 清空配置 # 清空配置
self._dirconf = {} self._dirconf = {}
self._transferconf = {} self._transferconf = {}

View File

@ -156,7 +156,7 @@ class DownloadingMsg(_PluginBase):
channel_value = downloadhis.channel channel_value = downloadhis.channel
else: else:
try: try:
context = MediaChain(self.db).recognize_by_title(title=torrent.title) context = MediaChain().recognize_by_title(title=torrent.title)
if not context or not context.media_info: if not context or not context.media_info:
continue continue
media_info = context.media_info media_info = context.media_info

View File

@ -67,7 +67,7 @@ class PersonMeta(_PluginBase):
_remove_nozh = False _remove_nozh = False
def init_plugin(self, config: dict = None): def init_plugin(self, config: dict = None):
self.tmdbchain = TmdbChain(self.db) self.tmdbchain = TmdbChain()
self.mschain = MediaServerChain(self.db) self.mschain = MediaServerChain(self.db)
if config: if config:
self._enabled = config.get("enabled") self._enabled = config.get("enabled")

View File

@ -40,6 +40,10 @@ class EventType(Enum):
UserMessage = "user.message" UserMessage = "user.message"
# 通知消息 # 通知消息
NoticeMessage = "notice.message" NoticeMessage = "notice.message"
# 名称识别请求
NameRecognize = "name.recognize"
# 名称识别结果
NameRecognizeResult = "name.recognize.result"
# 系统配置Key字典 # 系统配置Key字典