Merge remote-tracking branch 'origin/main'

This commit is contained in:
thsrite
2023-10-13 10:26:49 +08:00
12 changed files with 228 additions and 69 deletions

View File

@ -115,16 +115,18 @@ class ChainBase(metaclass=ABCMeta):
""" """
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 match_doubaninfo(self, name: str, mtype: str = None, def match_doubaninfo(self, name: str, imdbid: str = None,
year: str = None, season: int = None) -> Optional[dict]: mtype: str = None, year: str = None, season: int = None) -> Optional[dict]:
""" """
搜索和匹配豆瓣信息 搜索和匹配豆瓣信息
:param name: 标题 :param name: 标题
:param imdbid: imdbid
:param mtype: 类型 :param mtype: 类型
:param year: 年份 :param year: 年份
:param season: 季 :param season: 季
""" """
return self.run_module("match_doubaninfo", name=name, mtype=mtype, year=year, season=season) return self.run_module("match_doubaninfo", name=name, imdbid=imdbid,
mtype=mtype, year=year, season=season)
def obtain_images(self, mediainfo: MediaInfo) -> Optional[MediaInfo]: def obtain_images(self, mediainfo: MediaInfo) -> Optional[MediaInfo]:
""" """

View File

@ -6,6 +6,7 @@ from typing import Dict, List, Optional, Union, Tuple
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from app.chain import ChainBase from app.chain import ChainBase
from app.chain.douban import DoubanChain
from app.chain.download import DownloadChain from app.chain.download import DownloadChain
from app.chain.search import SearchChain from app.chain.search import SearchChain
from app.chain.torrents import TorrentsChain from app.chain.torrents import TorrentsChain
@ -50,18 +51,28 @@ class SubscribeChain(ChainBase):
识别媒体信息并添加订阅 识别媒体信息并添加订阅
""" """
logger.info(f'开始添加订阅,标题:{title} ...') logger.info(f'开始添加订阅,标题:{title} ...')
# 识别元数据 metainfo = None
metainfo = MetaInfo(title) mediainfo = None
if year: if not tmdbid and doubanid:
metainfo.year = year # 将豆瓣信息转换为TMDB信息
if mtype: context = DoubanChain().recognize_by_doubanid(doubanid)
metainfo.type = mtype if context:
if season: metainfo = context.meta_info
metainfo.type = MediaType.TV mediainfo = context.media_info
metainfo.begin_season = season else:
# 识别媒体信息 # 识别元数据
mediainfo: MediaInfo = self.recognize_media(meta=metainfo, mtype=mtype, tmdbid=tmdbid) metainfo = MetaInfo(title)
if not mediainfo: if year:
metainfo.year = year
if mtype:
metainfo.type = mtype
if season:
metainfo.type = MediaType.TV
metainfo.begin_season = season
# 识别媒体信息
mediainfo = self.recognize_media(meta=metainfo, mtype=mtype, tmdbid=tmdbid)
# 识别失败
if not mediainfo or not metainfo or not mediainfo.tmdb_id:
logger.warn(f'未识别到媒体信息,标题:{title}tmdbid{tmdbid}') logger.warn(f'未识别到媒体信息,标题:{title}tmdbid{tmdbid}')
return None, "未识别到媒体信息" return None, "未识别到媒体信息"
# 更新媒体图片 # 更新媒体图片
@ -74,8 +85,8 @@ class SubscribeChain(ChainBase):
if not kwargs.get('total_episode'): if not kwargs.get('total_episode'):
if not mediainfo.seasons: if not mediainfo.seasons:
# 补充媒体信息 # 补充媒体信息
mediainfo: MediaInfo = self.recognize_media(mtype=mediainfo.type, mediainfo = self.recognize_media(mtype=mediainfo.type,
tmdbid=mediainfo.tmdb_id) tmdbid=mediainfo.tmdb_id)
if not mediainfo: if not mediainfo:
logger.error(f"媒体信息识别失败!") logger.error(f"媒体信息识别失败!")
return None, "媒体信息识别失败" return None, "媒体信息识别失败"
@ -85,7 +96,7 @@ class SubscribeChain(ChainBase):
total_episode = len(mediainfo.seasons.get(season) or []) total_episode = len(mediainfo.seasons.get(season) or [])
if not total_episode: if not total_episode:
logger.error(f'未获取到总集数,标题:{title}tmdbid{tmdbid}') logger.error(f'未获取到总集数,标题:{title}tmdbid{tmdbid}')
return None, "未获取到总集数" return None, f"未获取到{season} 季的总集数"
kwargs.update({ kwargs.update({
'total_episode': total_episode 'total_episode': total_episode
}) })

View File

@ -61,8 +61,7 @@ class WordsMatcher(metaclass=Singleton):
if state: if state:
appley_words.append(word) appley_words.append(word)
else:
logger.debug(f"自定义识别词替换失败:{message}")
except Exception as err: except Exception as err:
print(str(err)) print(str(err))

View File

@ -1,4 +1,4 @@
from datetime import datetime import re
from pathlib import Path from pathlib import Path
from typing import List, Optional, Tuple, Union from typing import List, Optional, Tuple, Union
@ -406,17 +406,29 @@ class DoubanModule(_ModuleBase):
return ret_medias return ret_medias
@retry(Exception, 5, 3, 3, logger=logger) @retry(Exception, 5, 3, 3, logger=logger)
def match_doubaninfo(self, name: str, mtype: str = None, def match_doubaninfo(self, name: str, imdbid: str = None,
year: str = None, season: int = None) -> dict: mtype: str = None, year: str = None, season: int = None) -> dict:
""" """
搜索和匹配豆瓣信息 搜索和匹配豆瓣信息
:param name: 名称 :param name: 名称
:param imdbid: IMDB ID
:param mtype: 类型 电影/电视剧 :param mtype: 类型 电影/电视剧
:param year: 年份 :param year: 年份
:param season: 季号 :param season: 季号
""" """
result = self.doubanapi.search(f"{name} {year or ''}".strip(), if imdbid:
ts=datetime.strftime(datetime.now(), '%Y%m%d%H%M%S')) # 优先使用IMDBID查询
logger.info(f"开始使用IMDBID {imdbid} 查询豆瓣信息 ...")
result = self.doubanapi.imdbid(imdbid)
if result:
doubanid = result.get("id")
if doubanid and not str(doubanid).isdigit():
doubanid = re.search(r"\d+", doubanid).group(0)
result["id"] = doubanid
return result
# 搜索
logger.info(f"开始使用名称 {name} 查询豆瓣信息 ...")
result = self.doubanapi.search(f"{name} {year or ''}".strip())
if not result: if not result:
logger.warn(f"未找到 {name} 的豆瓣信息") logger.warn(f"未找到 {name} 的豆瓣信息")
return {} return {}
@ -473,12 +485,16 @@ class DoubanModule(_ModuleBase):
return return
# 根据名称查询豆瓣数据 # 根据名称查询豆瓣数据
doubaninfo = self.match_doubaninfo(name=mediainfo.title, doubaninfo = self.match_doubaninfo(name=mediainfo.title,
imdbid=mediainfo.imdb_id,
mtype=mediainfo.type.value, mtype=mediainfo.type.value,
year=mediainfo.year, year=mediainfo.year,
season=meta.begin_season) season=meta.begin_season)
if not doubaninfo: if not doubaninfo:
logger.warn(f"未找到 {mediainfo.title} 的豆瓣信息") logger.warn(f"未找到 {mediainfo.title} 的豆瓣信息")
return return
# 查询豆瓣详情
doubaninfo = self.douban_info(doubaninfo.get("id"))
# 刮削路径
scrape_path = path / path.name scrape_path = path / path.name
self.scraper.gen_scraper_files(meta=meta, self.scraper.gen_scraper_files(meta=meta,
mediainfo=MediaInfo(douban_info=doubaninfo), mediainfo=MediaInfo(douban_info=doubaninfo),
@ -495,12 +511,15 @@ class DoubanModule(_ModuleBase):
continue continue
# 根据名称查询豆瓣数据 # 根据名称查询豆瓣数据
doubaninfo = self.match_doubaninfo(name=mediainfo.title, doubaninfo = self.match_doubaninfo(name=mediainfo.title,
imdbid=mediainfo.imdb_id,
mtype=mediainfo.type.value, mtype=mediainfo.type.value,
year=mediainfo.year, year=mediainfo.year,
season=meta.begin_season) season=meta.begin_season)
if not doubaninfo: if not doubaninfo:
logger.warn(f"未找到 {mediainfo.title} 的豆瓣信息") logger.warn(f"未找到 {mediainfo.title} 的豆瓣信息")
break break
# 查询豆瓣详情
doubaninfo = self.douban_info(doubaninfo.get("id"))
# 刮削 # 刮削
self.scraper.gen_scraper_files(meta=meta, self.scraper.gen_scraper_files(meta=meta,
mediainfo=MediaInfo(douban_info=doubaninfo), mediainfo=MediaInfo(douban_info=doubaninfo),

View File

@ -18,28 +18,29 @@ class DoubanApi(metaclass=Singleton):
_urls = { _urls = {
# 搜索类 # 搜索类
# sort=U:近期热门 T:标记最多 S:评分最高 R:最新上映 # sort=U:近期热门 T:标记最多 S:评分最高 R:最新上映
# q=search_word&start=0&count=20&sort=U # q=search_word&start: int = 0&count: int = 20&sort=U
# 聚合搜索 # 聚合搜索
"search": "/search/weixin", "search": "/search/weixin",
"search_agg": "/search", "search_agg": "/search",
"imdbid": "/movie/imdb/%s",
# 电影探索 # 电影探索
# sort=U:综合排序 T:近期热度 S:高分优先 R:首播时间 # sort=U:综合排序 T:近期热度 S:高分优先 R:首播时间
# tags='日本,动画,2022'&start=0&count=20&sort=U # tags='日本,动画,2022'&start: int = 0&count: int = 20&sort=U
"movie_recommend": "/movie/recommend", "movie_recommend": "/movie/recommend",
# 电视剧探索 # 电视剧探索
"tv_recommend": "/tv/recommend", "tv_recommend": "/tv/recommend",
# 搜索 # 搜索
"movie_tag": "/movie/tag", "movie_tag": "/movie/tag",
"tv_tag": "/tv/tag", "tv_tag": "/tv/tag",
# q=search_word&start=0&count=20 # q=search_word&start: int = 0&count: int = 20
"movie_search": "/search/movie", "movie_search": "/search/movie",
"tv_search": "/search/movie", "tv_search": "/search/movie",
"book_search": "/search/book", "book_search": "/search/book",
"group_search": "/search/group", "group_search": "/search/group",
# 各类主题合集 # 各类主题合集
# start=0&count=20 # start: int = 0&count: int = 20
# 正在上映 # 正在上映
"movie_showing": "/subject_collection/movie_showing/items", "movie_showing": "/subject_collection/movie_showing/items",
# 热门电影 # 热门电影
@ -145,7 +146,9 @@ class DoubanApi(metaclass=Singleton):
"api-client/1 com.douban.frodo/7.3.0(207) Android/22 product/MI 9 vendor/Xiaomi model/MI 9 brand/Android rom/miui6 network/wifi platform/mobile nd/1"] "api-client/1 com.douban.frodo/7.3.0(207) Android/22 product/MI 9 vendor/Xiaomi model/MI 9 brand/Android rom/miui6 network/wifi platform/mobile nd/1"]
_api_secret_key = "bf7dddc7c9cfe6f7" _api_secret_key = "bf7dddc7c9cfe6f7"
_api_key = "0dad551ec0f84ed02907ff5c42e8ec70" _api_key = "0dad551ec0f84ed02907ff5c42e8ec70"
_api_key2 = "0ab215a8b1977939201640fa14c66bab"
_base_url = "https://frodo.douban.com/api/v2" _base_url = "https://frodo.douban.com/api/v2"
_api_url = "https://api.douban.com/v2"
_session = None _session = None
def __init__(self): def __init__(self):
@ -153,6 +156,9 @@ class DoubanApi(metaclass=Singleton):
@classmethod @classmethod
def __sign(cls, url: str, ts: int, method='GET') -> str: def __sign(cls, url: str, ts: int, method='GET') -> str:
"""
签名
"""
url_path = parse.urlparse(url).path url_path = parse.urlparse(url).path
raw_sign = '&'.join([method.upper(), parse.quote(url_path, safe=''), str(ts)]) raw_sign = '&'.join([method.upper(), parse.quote(url_path, safe=''), str(ts)])
return base64.b64encode( return base64.b64encode(
@ -164,7 +170,10 @@ class DoubanApi(metaclass=Singleton):
).decode() ).decode()
@lru_cache(maxsize=settings.CACHE_CONF.get('douban')) @lru_cache(maxsize=settings.CACHE_CONF.get('douban'))
def __invoke(self, url, **kwargs): def __invoke(self, url: str, **kwargs) -> dict:
"""
GET请求
"""
req_url = self._base_url + url req_url = self._base_url + url
params = {'apiKey': self._api_key} params = {'apiKey': self._api_key}
@ -189,119 +198,224 @@ class DoubanApi(metaclass=Singleton):
return resp.json() return resp.json()
return resp.json() if resp else {} return resp.json() if resp else {}
def search(self, keyword, start=0, count=20, @lru_cache(maxsize=settings.CACHE_CONF.get('douban'))
ts=datetime.strftime(datetime.now(), '%Y%m%d')): def __post(self, url: str, **kwargs) -> dict:
"""
POST请求
esponse = requests.post(
url="https://api.douban.com/v2/movie/imdb/tt29139455",
headers={
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
"Cookie": "bid=J9zb1zA5sJc",
},
data={
"apikey": "0ab215a8b1977939201640fa14c66bab",
},
)
"""
req_url = self._api_url + url
params = {'apikey': self._api_key2}
if kwargs:
params.update(kwargs)
if '_ts' in params:
params.pop('_ts')
resp = RequestUtils(
ua=settings.USER_AGENT,
session=self._session,
).post_res(url=req_url, data=params)
if resp.status_code == 400 and "rate_limit" in resp.text:
return resp.json()
return resp.json() if resp else {}
def search(self, keyword: str, start: int = 0, count: int = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')) -> dict:
"""
关键字搜索
"""
return self.__invoke(self._urls["search"], q=keyword, return self.__invoke(self._urls["search"], q=keyword,
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
def movie_search(self, keyword, start=0, count=20, def imdbid(self, imdbid: str,
ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
IMDBID搜索
"""
return self.__post(self._urls["imdbid"] % imdbid, _ts=ts)
def movie_search(self, keyword: str, start: int = 0, count: int = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
电影搜索
"""
return self.__invoke(self._urls["movie_search"], q=keyword, return self.__invoke(self._urls["movie_search"], q=keyword,
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
def tv_search(self, keyword, start=0, count=20, def tv_search(self, keyword: str, start: int = 0, count: int = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
电视搜索
"""
return self.__invoke(self._urls["tv_search"], q=keyword, return self.__invoke(self._urls["tv_search"], q=keyword,
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
def book_search(self, keyword, start=0, count=20, def book_search(self, keyword: str, start: int = 0, count: int = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
书籍搜索
"""
return self.__invoke(self._urls["book_search"], q=keyword, return self.__invoke(self._urls["book_search"], q=keyword,
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
def group_search(self, keyword, start=0, count=20, def group_search(self, keyword: str, start: int = 0, count: int = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
小组搜索
"""
return self.__invoke(self._urls["group_search"], q=keyword, return self.__invoke(self._urls["group_search"], q=keyword,
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
def movie_showing(self, start=0, count=20, def movie_showing(self, start: int = 0, count: int = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
正在热映
"""
return self.__invoke(self._urls["movie_showing"], return self.__invoke(self._urls["movie_showing"],
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
def movie_soon(self, start=0, count=20, def movie_soon(self, start: int = 0, count: int = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
即将上映
"""
return self.__invoke(self._urls["movie_soon"], return self.__invoke(self._urls["movie_soon"],
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
def movie_hot_gaia(self, start=0, count=20, def movie_hot_gaia(self, start: int = 0, count: int = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
热门电影
"""
return self.__invoke(self._urls["movie_hot_gaia"], return self.__invoke(self._urls["movie_hot_gaia"],
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
def tv_hot(self, start=0, count=20, def tv_hot(self, start: int = 0, count: int = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
热门剧集
"""
return self.__invoke(self._urls["tv_hot"], return self.__invoke(self._urls["tv_hot"],
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
def tv_animation(self, start=0, count=20, def tv_animation(self, start: int = 0, count: int = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
动画
"""
return self.__invoke(self._urls["tv_animation"], return self.__invoke(self._urls["tv_animation"],
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
def tv_variety_show(self, start=0, count=20, def tv_variety_show(self, start: int = 0, count: int = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
综艺
"""
return self.__invoke(self._urls["tv_variety_show"], return self.__invoke(self._urls["tv_variety_show"],
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
def tv_rank_list(self, start=0, count=20, def tv_rank_list(self, start: int = 0, count: int = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
电视剧排行榜
"""
return self.__invoke(self._urls["tv_rank_list"], return self.__invoke(self._urls["tv_rank_list"],
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
def show_hot(self, start=0, count=20, def show_hot(self, start: int = 0, count: int = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
综艺热门
"""
return self.__invoke(self._urls["show_hot"], return self.__invoke(self._urls["show_hot"],
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
def movie_detail(self, subject_id): def movie_detail(self, subject_id: str):
"""
电影详情
"""
return self.__invoke(self._urls["movie_detail"] + subject_id) return self.__invoke(self._urls["movie_detail"] + subject_id)
def movie_celebrities(self, subject_id): def movie_celebrities(self, subject_id: str):
"""
电影演职员
"""
return self.__invoke(self._urls["movie_celebrities"] % subject_id) return self.__invoke(self._urls["movie_celebrities"] % subject_id)
def tv_detail(self, subject_id): def tv_detail(self, subject_id: str):
"""
电视剧详情
"""
return self.__invoke(self._urls["tv_detail"] + subject_id) return self.__invoke(self._urls["tv_detail"] + subject_id)
def tv_celebrities(self, subject_id): def tv_celebrities(self, subject_id: str):
"""
电视剧演职员
"""
return self.__invoke(self._urls["tv_celebrities"] % subject_id) return self.__invoke(self._urls["tv_celebrities"] % subject_id)
def book_detail(self, subject_id): def book_detail(self, subject_id: str):
"""
书籍详情
"""
return self.__invoke(self._urls["book_detail"] + subject_id) return self.__invoke(self._urls["book_detail"] + subject_id)
def movie_top250(self, start=0, count=20, def movie_top250(self, start: int = 0, count: int = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
电影TOP250
"""
return self.__invoke(self._urls["movie_top250"], return self.__invoke(self._urls["movie_top250"],
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
def movie_recommend(self, tags='', sort='R', start=0, count=20, def movie_recommend(self, tags='', sort='R', start: int = 0, count: int = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
电影探索
"""
return self.__invoke(self._urls["movie_recommend"], tags=tags, sort=sort, return self.__invoke(self._urls["movie_recommend"], tags=tags, sort=sort,
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
def tv_recommend(self, tags='', sort='R', start=0, count=20, def tv_recommend(self, tags='', sort='R', start: int = 0, count: int = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
电视剧探索
"""
return self.__invoke(self._urls["tv_recommend"], tags=tags, sort=sort, return self.__invoke(self._urls["tv_recommend"], tags=tags, sort=sort,
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
def tv_chinese_best_weekly(self, start=0, count=20, def tv_chinese_best_weekly(self, start: int = 0, count: int = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
华语口碑周榜
"""
return self.__invoke(self._urls["tv_chinese_best_weekly"], return self.__invoke(self._urls["tv_chinese_best_weekly"],
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
def tv_global_best_weekly(self, start=0, count=20, def tv_global_best_weekly(self, start: int = 0, count: int = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
"""
全球口碑周榜
"""
return self.__invoke(self._urls["tv_global_best_weekly"], return self.__invoke(self._urls["tv_global_best_weekly"],
start=start, count=count, _ts=ts) start=start, count=count, _ts=ts)
def doulist_detail(self, subject_id): def doulist_detail(self, subject_id: str):
""" """
豆列详情 豆列详情
:param subject_id: 豆列id :param subject_id: 豆列id
""" """
return self.__invoke(self._urls["doulist"] + subject_id) return self.__invoke(self._urls["doulist"] + subject_id)
def doulist_items(self, subject_id, start=0, count=20, def doulist_items(self, subject_id: str, start: int = 0, count: int = 20,
ts=datetime.strftime(datetime.now(), '%Y%m%d')): ts=datetime.strftime(datetime.now(), '%Y%m%d')):
""" """
豆列列表 豆列列表

View File

@ -382,13 +382,14 @@ class FileTransferModule(_ModuleBase):
path=in_path, path=in_path,
message=f"{in_path} 路径不存在") message=f"{in_path} 路径不存在")
if not target_dir.exists(): if transfer_type not in ['rclone_copy', 'rclone_move']:
return TransferInfo(success=False, # 检查目标路径
path=in_path, if not target_dir.exists():
message=f"{target_dir} 目标路径不存在") return TransferInfo(success=False,
path=in_path,
# 媒体库目的目录 message=f"{target_dir} 目标路径不存在")
target_dir = self.__get_dest_dir(mediainfo=mediainfo, target_dir=target_dir) # 媒体库目的目录
target_dir = self.__get_dest_dir(mediainfo=mediainfo, target_dir=target_dir)
# 重命名格式 # 重命名格式
rename_format = settings.TV_RENAME_FORMAT \ rename_format = settings.TV_RENAME_FORMAT \
@ -401,6 +402,8 @@ class FileTransferModule(_ModuleBase):
bluray_flag = SystemUtils.is_bluray_dir(in_path) bluray_flag = SystemUtils.is_bluray_dir(in_path)
if bluray_flag: if bluray_flag:
logger.info(f"{in_path} 是蓝光原盘文件夹") logger.info(f"{in_path} 是蓝光原盘文件夹")
# 原文件大小
file_size = in_path.stat().st_size
# 目的路径 # 目的路径
new_path = self.get_rename_path( new_path = self.get_rename_path(
path=target_dir, path=target_dir,
@ -425,7 +428,7 @@ class FileTransferModule(_ModuleBase):
return TransferInfo(success=True, return TransferInfo(success=True,
path=in_path, path=in_path,
target_path=new_path, target_path=new_path,
total_size=new_path.stat().st_size, total_size=file_size,
is_bluray=bluray_flag) is_bluray=bluray_flag)
else: else:
# 转移单个文件 # 转移单个文件
@ -466,7 +469,8 @@ class FileTransferModule(_ModuleBase):
if new_file.stat().st_size < in_path.stat().st_size: if new_file.stat().st_size < in_path.stat().st_size:
logger.info(f"目标文件已存在,但文件大小更小,将覆盖:{new_file}") logger.info(f"目标文件已存在,但文件大小更小,将覆盖:{new_file}")
overflag = True overflag = True
# 原文件大小
file_size = in_path.stat().st_size
# 转移文件 # 转移文件
retcode = self.__transfer_file(file_item=in_path, retcode = self.__transfer_file(file_item=in_path,
new_file=new_file, new_file=new_file,
@ -485,7 +489,7 @@ class FileTransferModule(_ModuleBase):
path=in_path, path=in_path,
target_path=new_file, target_path=new_file,
file_count=1, file_count=1,
total_size=new_file.stat().st_size, total_size=file_size,
is_bluray=False, is_bluray=False,
file_list=[str(in_path)], file_list=[str(in_path)],
file_list_new=[str(new_file)]) file_list_new=[str(new_file)])

View File

@ -103,7 +103,12 @@ class FilterModule(_ModuleBase):
"CNVOI": { "CNVOI": {
"include": [r'[国國][语語]配音|[国國]配|[国國][语語]'], "include": [r'[国國][语語]配音|[国國]配|[国國][语語]'],
"exclude": [] "exclude": []
} },
# 60FPS
"60FPS": {
"include": [r'60fps'],
"exclude": []
},
} }
def init_module(self) -> None: def init_module(self) -> None:

View File

@ -283,6 +283,8 @@ class TheMovieDbModule(_ModuleBase):
:param mediainfo: 识别的媒体信息 :param mediainfo: 识别的媒体信息
:return: 更新后的媒体信息 :return: 更新后的媒体信息
""" """
if not mediainfo.tmdb_id:
return mediainfo
if mediainfo.logo_path \ if mediainfo.logo_path \
and mediainfo.poster_path \ and mediainfo.poster_path \
and mediainfo.backdrop_path: and mediainfo.backdrop_path:

View File

@ -251,6 +251,6 @@ class ChineseSubFinder(_PluginBase):
else: else:
logger.info("ChineseSubFinder任务添加成功%s" % job_id) logger.info("ChineseSubFinder任务添加成功%s" % job_id)
else: else:
logger.error("%s 目录缺失nfo元数据" % file_path) logger.warn(f"ChineseSubFinder调用出错{res.status_code} - {res.reason}")
except Exception as e: except Exception as e:
logger.error("连接ChineseSubFinder出错" + str(e)) logger.error("连接ChineseSubFinder出错" + str(e))

View File

@ -587,6 +587,7 @@ class PersonMeta(_PluginBase):
time.sleep(sleep_time) time.sleep(sleep_time)
# 匹配豆瓣信息 # 匹配豆瓣信息
doubaninfo = self.chain.match_doubaninfo(name=mediainfo.title, doubaninfo = self.chain.match_doubaninfo(name=mediainfo.title,
imdbid=mediainfo.imdb_id,
mtype=mediainfo.type.value, mtype=mediainfo.type.value,
year=mediainfo.year, year=mediainfo.year,
season=season) season=season)

View File

@ -99,6 +99,8 @@ class SystemUtils:
try: try:
# link到当前目录并改名 # link到当前目录并改名
tmp_path = src.parent / (dest.name + ".mp") tmp_path = src.parent / (dest.name + ".mp")
if tmp_path.exists():
tmp_path.unlink()
tmp_path.hardlink_to(src) tmp_path.hardlink_to(src)
# 移动到目标目录 # 移动到目标目录
shutil.move(tmp_path, dest) shutil.move(tmp_path, dest)

View File

@ -1 +1 @@
APP_VERSION = 'v1.3.1' APP_VERSION = 'v1.3.2-1'