init
This commit is contained in:
58
app/chain/__init__.py
Normal file
58
app/chain/__init__.py
Normal file
@ -0,0 +1,58 @@
|
||||
from abc import abstractmethod
|
||||
from typing import Optional, Any
|
||||
|
||||
from app.core import Context, ModuleManager, EventManager
|
||||
from app.log import logger
|
||||
from app.utils.singleton import AbstractSingleton, Singleton
|
||||
|
||||
|
||||
class _ChainBase(AbstractSingleton, metaclass=Singleton):
|
||||
"""
|
||||
处理链基类
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
公共初始化
|
||||
"""
|
||||
self.modulemanager = ModuleManager()
|
||||
self.eventmanager = EventManager()
|
||||
|
||||
@abstractmethod
|
||||
def process(self, *args, **kwargs) -> Optional[Context]:
|
||||
"""
|
||||
处理链,返回上下文
|
||||
"""
|
||||
pass
|
||||
|
||||
def run_module(self, method: str, *args, **kwargs) -> Any:
|
||||
"""
|
||||
运行包含该方法的所有模块,然后返回结果
|
||||
"""
|
||||
|
||||
def is_result_empty(ret):
|
||||
"""
|
||||
判断结果是否为空
|
||||
"""
|
||||
if isinstance(ret, tuple):
|
||||
return all(value is None for value in ret)
|
||||
else:
|
||||
return result is None
|
||||
|
||||
logger.info(f"请求模块执行:{method} ...")
|
||||
result = None
|
||||
modules = self.modulemanager.get_modules(method)
|
||||
for module in modules:
|
||||
try:
|
||||
if is_result_empty(result):
|
||||
result = getattr(module, method)(*args, **kwargs)
|
||||
else:
|
||||
if isinstance(result, tuple):
|
||||
temp = getattr(module, method)(*result)
|
||||
else:
|
||||
temp = getattr(module, method)(result)
|
||||
if temp:
|
||||
result = temp
|
||||
except Exception as err:
|
||||
logger.error(f"运行模块出错:{module.__class__.__name__} - {err}")
|
||||
return result
|
BIN
app/chain/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
app/chain/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
app/chain/__pycache__/common.cpython-310.pyc
Normal file
BIN
app/chain/__pycache__/common.cpython-310.pyc
Normal file
Binary file not shown.
BIN
app/chain/__pycache__/cookiecloud.cpython-310.pyc
Normal file
BIN
app/chain/__pycache__/cookiecloud.cpython-310.pyc
Normal file
Binary file not shown.
BIN
app/chain/__pycache__/douban_sync.cpython-310.pyc
Normal file
BIN
app/chain/__pycache__/douban_sync.cpython-310.pyc
Normal file
Binary file not shown.
BIN
app/chain/__pycache__/identify.cpython-310.pyc
Normal file
BIN
app/chain/__pycache__/identify.cpython-310.pyc
Normal file
Binary file not shown.
BIN
app/chain/__pycache__/search.cpython-310.pyc
Normal file
BIN
app/chain/__pycache__/search.cpython-310.pyc
Normal file
Binary file not shown.
BIN
app/chain/__pycache__/subscribe.cpython-310.pyc
Normal file
BIN
app/chain/__pycache__/subscribe.cpython-310.pyc
Normal file
Binary file not shown.
BIN
app/chain/__pycache__/user_message.cpython-310.pyc
Normal file
BIN
app/chain/__pycache__/user_message.cpython-310.pyc
Normal file
Binary file not shown.
BIN
app/chain/__pycache__/webhook_message.cpython-310.pyc
Normal file
BIN
app/chain/__pycache__/webhook_message.cpython-310.pyc
Normal file
Binary file not shown.
383
app/chain/common.py
Normal file
383
app/chain/common.py
Normal file
@ -0,0 +1,383 @@
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import List, Optional, Tuple, Set
|
||||
|
||||
from app.chain import _ChainBase
|
||||
from app.core import MediaInfo
|
||||
from app.core import TorrentInfo, Context
|
||||
from app.core.meta import MetaBase
|
||||
from app.helper.torrent import TorrentHelper
|
||||
from app.log import logger
|
||||
from app.utils.string import StringUtils
|
||||
from app.utils.types import MediaType
|
||||
|
||||
|
||||
class CommonChain(_ChainBase):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.torrent = TorrentHelper()
|
||||
|
||||
def process(self, *args, **kwargs) -> Optional[Context]:
|
||||
pass
|
||||
|
||||
def post_message(self, title: str, text: str = None, image: str = None, userid: str = None):
|
||||
"""
|
||||
发送消息
|
||||
"""
|
||||
self.run_module('post_message', title=title, text=text, image=image, userid=userid)
|
||||
|
||||
def post_download_message(self, meta: MetaBase, mediainfo: MediaInfo, torrent: TorrentInfo, userid: str = None):
|
||||
"""
|
||||
发送添加下载的消息
|
||||
"""
|
||||
msg_text = ""
|
||||
if torrent.site_name:
|
||||
msg_text = f"站点:{torrent.site_name}"
|
||||
if meta.get_resource_type_string():
|
||||
msg_text = f"{msg_text}\n质量:{meta.get_resource_type_string()}"
|
||||
if torrent.size:
|
||||
if str(torrent.size).isdigit():
|
||||
size = StringUtils.str_filesize(torrent.size)
|
||||
else:
|
||||
size = torrent.size
|
||||
msg_text = f"{msg_text}\n大小:{size}"
|
||||
if torrent.title:
|
||||
msg_text = f"{msg_text}\n种子:{torrent.title}"
|
||||
if torrent.seeders:
|
||||
msg_text = f"{msg_text}\n做种数:{torrent.seeders}"
|
||||
msg_text = f"{msg_text}\n促销:{torrent.get_volume_factor_string()}"
|
||||
if torrent.hit_and_run:
|
||||
msg_text = f"{msg_text}\nHit&Run:是"
|
||||
if torrent.description:
|
||||
html_re = re.compile(r'<[^>]+>', re.S)
|
||||
description = html_re.sub('', torrent.description)
|
||||
torrent.description = re.sub(r'<[^>]+>', '', description)
|
||||
msg_text = f"{msg_text}\n描述:{torrent.description}"
|
||||
|
||||
self.post_message(title=f"{mediainfo.get_title_string()}"
|
||||
f"{meta.get_season_episode_string()} 开始下载",
|
||||
text=msg_text,
|
||||
image=mediainfo.get_message_image(),
|
||||
userid=userid)
|
||||
|
||||
def batch_download(self,
|
||||
contexts: List[Context],
|
||||
need_tvs: dict = None,
|
||||
userid: str = None) -> Tuple[List[Context], dict]:
|
||||
"""
|
||||
根据缺失数据,自动种子列表中组合择优下载
|
||||
:param contexts: 资源上下文列表
|
||||
:param need_tvs: 缺失的剧集信息
|
||||
:param userid: 用户ID
|
||||
:return: 已经下载的资源列表、剩余未下载到的剧集 no_exists[mediainfo.tmdb_id] = [
|
||||
{
|
||||
"season": season,
|
||||
"episodes": episodes,
|
||||
"total_episodes": len(episodes)
|
||||
}
|
||||
]
|
||||
"""
|
||||
# 已下载的项目
|
||||
downloaded_list: list = []
|
||||
|
||||
def __download_torrent(_torrent: TorrentInfo) -> Tuple[Optional[Path], list]:
|
||||
"""
|
||||
下载种子文件
|
||||
:return: 种子路径,种子文件清单
|
||||
"""
|
||||
torrent_file, _, _, files, error_msg = self.torrent.download_torrent(
|
||||
url=_torrent.enclosure,
|
||||
cookie=_torrent.site_cookie,
|
||||
ua=_torrent.site_ua,
|
||||
proxy=_torrent.site_proxy)
|
||||
if not torrent_file:
|
||||
logger.error(f"下载种子文件失败:{_torrent.title} - {_torrent.enclosure}")
|
||||
self.run_module('post_message',
|
||||
title=f"{_torrent.title} 种子下载失败!",
|
||||
text=f"错误信息:{error_msg}\n种子链接:{_torrent.enclosure}",
|
||||
userid=userid)
|
||||
return None, []
|
||||
return torrent_file, files
|
||||
|
||||
def __download(_context: Context, _torrent_file: Path = None, _episodes: Set[int] = None) -> Optional[str]:
|
||||
"""
|
||||
下载及发送通知
|
||||
"""
|
||||
_torrent = _context.torrent_info
|
||||
_media = _context.media_info
|
||||
_meta = _context.meta_info
|
||||
if not _torrent_file:
|
||||
# 下载种子文件
|
||||
_torrent_file, _ = __download_torrent(_torrent)
|
||||
if not _torrent_file:
|
||||
return
|
||||
# 添加下载
|
||||
_hash, error_msg = self.run_module("download",
|
||||
torrent_path=_torrent_file,
|
||||
mediainfo=_media,
|
||||
episodes=_episodes)
|
||||
if _hash:
|
||||
# 下载成功
|
||||
downloaded_list.append(_context)
|
||||
self.post_download_message(meta=_meta, mediainfo=_media, torrent=_torrent, userid=userid)
|
||||
else:
|
||||
# 下载失败
|
||||
logger.error(f"{_media.get_title_string()} 添加下载任务失败:"
|
||||
f"{_torrent.title} - {_torrent.enclosure},{error_msg}")
|
||||
self.run_module('post_message',
|
||||
title="添加下载任务失败:%s %s"
|
||||
% (_media.get_title_string(), _meta.get_season_episode_string()),
|
||||
text=f"站点:{_torrent.site_name}\n"
|
||||
f"种子名称:{_meta.org_string}\n"
|
||||
f"种子链接:{_torrent.enclosure}\n"
|
||||
f"错误信息:{error_msg}",
|
||||
image=_media.get_message_image(),
|
||||
userid=userid)
|
||||
return _hash
|
||||
|
||||
def __update_seasons(tmdbid, need, current):
|
||||
"""
|
||||
更新need_tvs季数
|
||||
"""
|
||||
need = list(set(need).difference(set(current)))
|
||||
for cur in current:
|
||||
for nt in need_tvs.get(tmdbid):
|
||||
if cur == nt.get("season") or (cur == 1 and not nt.get("season")):
|
||||
need_tvs[tmdbid].remove(nt)
|
||||
if not need_tvs.get(tmdbid):
|
||||
need_tvs.pop(tmdbid)
|
||||
return need
|
||||
|
||||
def __update_episodes(tmdbid, seq, need, current):
|
||||
"""
|
||||
更新need_tvs集数
|
||||
"""
|
||||
need = list(set(need).difference(set(current)))
|
||||
if need:
|
||||
need_tvs[tmdbid][seq]["episodes"] = need
|
||||
else:
|
||||
need_tvs[tmdbid].pop(seq)
|
||||
if not need_tvs.get(tmdbid):
|
||||
need_tvs.pop(tmdbid)
|
||||
return need
|
||||
|
||||
def __get_season_episodes(tmdbid, season):
|
||||
"""
|
||||
获取需要的季的集数
|
||||
"""
|
||||
if not need_tvs.get(tmdbid):
|
||||
return 0
|
||||
for nt in need_tvs.get(tmdbid):
|
||||
if season == nt.get("season"):
|
||||
return nt.get("total_episodes")
|
||||
return 0
|
||||
|
||||
# 如果是电影,直接下载
|
||||
for context in contexts:
|
||||
if context.media_info.type == MediaType.MOVIE:
|
||||
__download(context)
|
||||
|
||||
# 电视剧整季匹配
|
||||
if need_tvs:
|
||||
# 先把整季缺失的拿出来,看是否刚好有所有季都满足的种子
|
||||
need_seasons = {}
|
||||
for need_tmdbid, need_tv in need_tvs.items():
|
||||
for tv in need_tv:
|
||||
if not tv:
|
||||
continue
|
||||
if not tv.get("episodes"):
|
||||
if not need_seasons.get(need_tmdbid):
|
||||
need_seasons[need_tmdbid] = []
|
||||
need_seasons[need_tmdbid].append(tv.get("season") or 1)
|
||||
# 查找整季包含的种子,只处理整季没集的种子或者是集数超过季的种子
|
||||
for need_tmdbid, need_season in need_seasons.items():
|
||||
for context in contexts:
|
||||
media = context.media_info
|
||||
meta = context.meta_info
|
||||
torrent = context.torrent_info
|
||||
if media.type == MediaType.MOVIE:
|
||||
continue
|
||||
item_season = meta.get_season_list()
|
||||
if meta.get_episode_list():
|
||||
continue
|
||||
if need_tmdbid == media.tmdb_id:
|
||||
if set(item_season).issubset(set(need_season)):
|
||||
if len(item_season) == 1:
|
||||
# 只有一季的可能是命名错误,需要打开种子鉴别,只有实际集数大于等于总集数才下载
|
||||
torrent_path, torrent_files = __download_torrent(torrent)
|
||||
if not torrent_path:
|
||||
continue
|
||||
torrent_episodes = self.torrent.get_torrent_episodes(torrent_files)
|
||||
if not torrent_episodes \
|
||||
or len(torrent_episodes) >= __get_season_episodes(need_tmdbid, item_season[0]):
|
||||
_, download_id = __download(_context=context, _torrent_file=torrent_path)
|
||||
else:
|
||||
logger.info(
|
||||
f"【Downloader】种子 {meta.org_string} 未含集数信息,解析文件数为 {len(torrent_episodes)}")
|
||||
continue
|
||||
else:
|
||||
download_id = __download(context)
|
||||
if download_id:
|
||||
# 更新仍需季集
|
||||
need_season = __update_seasons(tmdbid=need_tmdbid,
|
||||
need=need_season,
|
||||
current=item_season)
|
||||
# 电视剧季内的集匹配
|
||||
if need_tvs:
|
||||
need_tv_list = list(need_tvs)
|
||||
for need_tmdbid in need_tv_list:
|
||||
need_tv = need_tvs.get(need_tmdbid)
|
||||
if not need_tv:
|
||||
continue
|
||||
index = 0
|
||||
for tv in need_tv:
|
||||
need_season = tv.get("season") or 1
|
||||
need_episodes = tv.get("episodes")
|
||||
total_episodes = tv.get("total_episodes")
|
||||
# 缺失整季的转化为缺失集进行比较
|
||||
if not need_episodes:
|
||||
need_episodes = list(range(1, total_episodes + 1))
|
||||
for context in contexts:
|
||||
media = context.media_info
|
||||
meta = context.meta_info
|
||||
if media.type == MediaType.MOVIE:
|
||||
continue
|
||||
if media.tmdb_id == need_tmdbid:
|
||||
if context in downloaded_list:
|
||||
continue
|
||||
# 只处理单季含集的种子
|
||||
item_season = meta.get_season_list()
|
||||
if len(item_season) != 1 or item_season[0] != need_season:
|
||||
continue
|
||||
item_episodes = meta.get_episode_list()
|
||||
if not item_episodes:
|
||||
continue
|
||||
# 为需要集的子集则下载
|
||||
if set(item_episodes).issubset(set(need_episodes)):
|
||||
download_id = __download(context)
|
||||
if download_id:
|
||||
# 更新仍需集数
|
||||
need_episodes = __update_episodes(tmdbid=need_tmdbid,
|
||||
need=need_episodes,
|
||||
seq=index,
|
||||
current=item_episodes)
|
||||
index += 1
|
||||
|
||||
# 仍然缺失的剧集,从整季中选择需要的集数文件下载,仅支持QB和TR
|
||||
if need_tvs:
|
||||
need_tv_list = list(need_tvs)
|
||||
for need_tmdbid in need_tv_list:
|
||||
need_tv = need_tvs.get(need_tmdbid)
|
||||
if not need_tv:
|
||||
continue
|
||||
index = 0
|
||||
for tv in need_tv:
|
||||
need_season = tv.get("season") or 1
|
||||
need_episodes = tv.get("episodes")
|
||||
if not need_episodes:
|
||||
continue
|
||||
for context in contexts:
|
||||
media = context.media_info
|
||||
meta = context.meta_info
|
||||
torrent = context.torrent_info
|
||||
if media.type == MediaType.MOVIE:
|
||||
continue
|
||||
if context in downloaded_list:
|
||||
continue
|
||||
if not need_episodes:
|
||||
break
|
||||
# 选中一个单季整季的或单季包括需要的所有集的
|
||||
if media.tmdb_id == need_tmdbid \
|
||||
and (not meta.get_episode_list()
|
||||
or set(meta.get_episode_list()).intersection(set(need_episodes))) \
|
||||
and len(meta.get_season_list()) == 1 \
|
||||
and meta.get_season_list()[0] == need_season:
|
||||
# 检查种子看是否有需要的集
|
||||
torrent_path, torrent_files = __download_torrent(torrent)
|
||||
if not torrent_path:
|
||||
continue
|
||||
# 种子全部集
|
||||
torrent_episodes = self.torrent.get_torrent_episodes(torrent_files)
|
||||
# 选中的集
|
||||
selected_episodes = set(torrent_episodes).intersection(set(need_episodes))
|
||||
if not selected_episodes:
|
||||
logger.info(f"{meta.org_string} 没有需要的集,跳过...")
|
||||
continue
|
||||
# 添加下载
|
||||
download_id = __download(_context=context,
|
||||
_torrent_file=torrent_path,
|
||||
_episodes=selected_episodes)
|
||||
if not download_id:
|
||||
continue
|
||||
# 更新仍需集数
|
||||
need_episodes = __update_episodes(tmdbid=need_tmdbid,
|
||||
need=need_episodes,
|
||||
seq=index,
|
||||
current=selected_episodes)
|
||||
index += 1
|
||||
|
||||
# 返回下载的资源,剩下没下完的
|
||||
return downloaded_list, need_tvs
|
||||
|
||||
def get_no_exists_info(self, mediainfo: MediaInfo, no_exists: dict = None) -> Tuple[bool, dict]:
|
||||
"""
|
||||
检查媒体库,查询是否存在,对于剧集同时返回不存在的季集信息
|
||||
:param mediainfo: 已识别的媒体信息
|
||||
:param no_exists: 在调用该方法前已经存储的不存在的季集信息,有传入时该函数搜索的内容将会叠加后输出
|
||||
:return: 当前媒体是否缺失,各标题总的季集和缺失的季集
|
||||
"""
|
||||
def __append_no_exists(_season: int, _episodes: list):
|
||||
"""
|
||||
添加不存在的季集信息
|
||||
"""
|
||||
if not no_exists.get(mediainfo.tmdb_id):
|
||||
no_exists[mediainfo.tmdb_id] = [
|
||||
{
|
||||
"season": season,
|
||||
"episodes": episodes,
|
||||
"total_episodes": len(episodes)
|
||||
}
|
||||
]
|
||||
else:
|
||||
no_exists[mediainfo.tmdb_id].append({
|
||||
"season": season,
|
||||
"episodes": episodes,
|
||||
"total_episodes": len(episodes)
|
||||
})
|
||||
|
||||
if not no_exists:
|
||||
no_exists = {}
|
||||
if not mediainfo.seasons:
|
||||
logger.error(f"媒体信息中没有季集信息:{mediainfo.get_title_string()}")
|
||||
return False, {}
|
||||
if mediainfo.type == MediaType.MOVIE:
|
||||
# 电影
|
||||
exists_movies = self.run_module("media_exists", mediainfo)
|
||||
if exists_movies:
|
||||
logger.info(f"媒体库中已存在电影:{mediainfo.get_title_string()}")
|
||||
return True, {}
|
||||
return False, {}
|
||||
else:
|
||||
# 电视剧
|
||||
exists_tvs = self.run_module("media_exists", mediainfo)
|
||||
if not exists_tvs:
|
||||
# 所有剧集均缺失
|
||||
for season, episodes in mediainfo.seasons.items():
|
||||
# 添加不存在的季集信息
|
||||
__append_no_exists(season, episodes)
|
||||
return False, no_exists
|
||||
else:
|
||||
# 存在一些,检查缺失的季集
|
||||
for season, episodes in mediainfo.seasons.items():
|
||||
exist_seasons = exists_tvs.get("seasons")
|
||||
if exist_seasons.get(season):
|
||||
# 取差集
|
||||
episodes = set(episodes).difference(set(exist_seasons['season']))
|
||||
# 添加不存在的季集信息
|
||||
__append_no_exists(season, episodes)
|
||||
# 存在不完整的剧集
|
||||
if no_exists:
|
||||
return False, no_exists
|
||||
# 全部存在
|
||||
return True, no_exists
|
55
app/chain/cookiecloud.py
Normal file
55
app/chain/cookiecloud.py
Normal file
@ -0,0 +1,55 @@
|
||||
from typing import Tuple
|
||||
|
||||
from app.chain import _ChainBase
|
||||
from app.core import settings
|
||||
from app.db.sites import Sites
|
||||
from app.helper.cookiecloud import CookieCloudHelper
|
||||
from app.helper.sites import SitesHelper
|
||||
from app.log import logger
|
||||
|
||||
|
||||
class CookieCloudChain(_ChainBase):
|
||||
"""
|
||||
同步站点Cookie
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.sites = Sites()
|
||||
self.siteshelper = SitesHelper()
|
||||
self.cookiecloud = CookieCloudHelper(
|
||||
server=settings.COOKIECLOUD_HOST,
|
||||
key=settings.COOKIECLOUD_KEY,
|
||||
password=settings.COOKIECLOUD_PASSWORD
|
||||
)
|
||||
|
||||
def process(self) -> Tuple[bool, str]:
|
||||
"""
|
||||
通过CookieCloud同步站点Cookie
|
||||
"""
|
||||
logger.info("开始同步CookieCloud站点 ...")
|
||||
cookies, msg = self.cookiecloud.download()
|
||||
if not cookies:
|
||||
logger.error(f"CookieCloud同步失败:{msg}")
|
||||
return False, msg
|
||||
# 保存Cookie或新增站点
|
||||
_update_count = 0
|
||||
_add_count = 0
|
||||
for domain, cookie in cookies.items():
|
||||
if self.sites.exists(domain):
|
||||
# 更新站点Cookie
|
||||
self.sites.update_cookie(domain, cookie)
|
||||
_update_count += 1
|
||||
else:
|
||||
# 获取站点信息
|
||||
indexer = self.siteshelper.get_indexer(domain)
|
||||
if indexer:
|
||||
# 新增站点
|
||||
self.sites.add(name=indexer.get("name"),
|
||||
url=indexer.get("domain"),
|
||||
domain=domain,
|
||||
cookie=cookie)
|
||||
_add_count += 1
|
||||
ret_msg = f"更新了{_update_count}个站点,新增了{_add_count}个站点"
|
||||
logger.info(f"CookieCloud同步成功:{ret_msg}")
|
||||
return True, ret_msg
|
100
app/chain/douban_sync.py
Normal file
100
app/chain/douban_sync.py
Normal file
@ -0,0 +1,100 @@
|
||||
from pathlib import Path
|
||||
|
||||
from app.chain import _ChainBase
|
||||
from app.chain.common import CommonChain
|
||||
from app.chain.search import SearchChain
|
||||
from app.core import settings, MetaInfo
|
||||
from app.db.subscribes import Subscribes
|
||||
from app.helper.rss import RssHelper
|
||||
from app.log import logger
|
||||
|
||||
|
||||
class DoubanSyncChain(_ChainBase):
|
||||
"""
|
||||
同步豆瓣相看数据
|
||||
"""
|
||||
|
||||
_interests_url: str = "https://www.douban.com/feed/people/%s/interests"
|
||||
|
||||
_cache_path: Path = settings.TEMP_PATH / "__doubansync_cache__"
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.rsshelper = RssHelper()
|
||||
self.common = CommonChain()
|
||||
self.searchchain = SearchChain()
|
||||
self.subscribes = Subscribes()
|
||||
|
||||
def process(self):
|
||||
"""
|
||||
通过用户RSS同步豆瓣相看数据
|
||||
"""
|
||||
if not settings.DOUBAN_USER_IDS:
|
||||
return
|
||||
# 读取缓存
|
||||
caches = self._cache_path.read_text().split("\n") if self._cache_path.exists() else []
|
||||
for user_id in settings.DOUBAN_USER_IDS.split(","):
|
||||
# 同步每个用户的豆瓣数据
|
||||
if not user_id:
|
||||
continue
|
||||
logger.info(f"开始同步用户 {user_id} 的豆瓣想看数据 ...")
|
||||
url = self._interests_url % user_id
|
||||
results = self.rsshelper.parse(url)
|
||||
if not results:
|
||||
logger.error(f"未获取到用户 {user_id} 豆瓣RSS数据:{url}")
|
||||
return
|
||||
# 解析数据
|
||||
for result in results:
|
||||
dtype = result.get("title", "")[:2]
|
||||
title = result.get("title", "")[2:]
|
||||
if dtype not in ["想看"]:
|
||||
continue
|
||||
douban_id = result.get("link", "").split("/")[-2]
|
||||
if not douban_id or douban_id in caches:
|
||||
continue
|
||||
# 根据豆瓣ID获取豆瓣数据
|
||||
doubaninfo = self.run_module('douban_info', doubanid=douban_id)
|
||||
if not doubaninfo:
|
||||
logger.warn(f'未获取到豆瓣信息,标题:{title},豆瓣ID:{douban_id}')
|
||||
continue
|
||||
# 识别媒体信息
|
||||
meta = MetaInfo(doubaninfo.get("original_title") or doubaninfo.get("title"))
|
||||
if doubaninfo.get("year"):
|
||||
meta.year = doubaninfo.get("year")
|
||||
mediainfo = self.run_module('recognize_media', meta=meta)
|
||||
if not mediainfo:
|
||||
logger.warn(f'未识别到媒体信息,标题:{title},豆瓣ID:{douban_id}')
|
||||
continue
|
||||
# 加入缓存
|
||||
caches.append(douban_id)
|
||||
# 查询缺失的媒体信息
|
||||
exist_flag, no_exists = self.common.get_no_exists_info(mediainfo=mediainfo)
|
||||
if exist_flag:
|
||||
logger.info(f'{mediainfo.get_title_string()} 媒体库中已存在')
|
||||
continue
|
||||
# 搜索
|
||||
contexts = self.searchchain.process(meta=meta, mediainfo=mediainfo)
|
||||
if not contexts:
|
||||
logger.warn(f'{mediainfo.get_title_string()} 未搜索到资源')
|
||||
continue
|
||||
# 自动下载
|
||||
_, lefts = self.common.batch_download(contexts=contexts, need_tvs=no_exists)
|
||||
if not lefts:
|
||||
# 全部下载完成
|
||||
logger.info(f'{mediainfo.get_title_string()} 下载完成')
|
||||
else:
|
||||
# 未完成下载
|
||||
logger.info(f'{mediainfo.get_title_string()} 未下载未完整,添加订阅 ...')
|
||||
# 添加订阅
|
||||
state, msg = self.subscribes.add(mediainfo,
|
||||
season=meta.begin_season)
|
||||
if state:
|
||||
# 订阅成功
|
||||
self.common.post_message(
|
||||
title=f"{mediainfo.get_title_string()} 已添加订阅",
|
||||
text="来自:豆瓣相看",
|
||||
image=mediainfo.get_message_image())
|
||||
|
||||
logger.info(f"用户 {user_id} 豆瓣相看同步完成")
|
||||
# 保存缓存
|
||||
self._cache_path.write_text("\n".join(caches))
|
33
app/chain/identify.py
Normal file
33
app/chain/identify.py
Normal file
@ -0,0 +1,33 @@
|
||||
from typing import Optional
|
||||
|
||||
from app.chain import _ChainBase
|
||||
from app.core import Context, MetaInfo, 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 = self.run_module('prepare_recognize', title=title, subtitle=subtitle)
|
||||
if result:
|
||||
title, subtitle = result
|
||||
# 识别元数据
|
||||
metainfo = MetaInfo(title, subtitle)
|
||||
# 识别媒体信息
|
||||
mediainfo: MediaInfo = self.run_module('recognize_media', meta=metainfo)
|
||||
if not mediainfo:
|
||||
logger.warn(f'{title} 未识别到媒体信息')
|
||||
return Context(meta=metainfo)
|
||||
logger.info(f'{title} 识别到媒体信息:{mediainfo.type.value} {mediainfo.get_title_string()}')
|
||||
# 更新媒体图片
|
||||
self.run_module('obtain_image', mediainfo=mediainfo)
|
||||
# 返回上下文
|
||||
return Context(meta=metainfo, mediainfo=mediainfo, title=title, subtitle=subtitle)
|
73
app/chain/search.py
Normal file
73
app/chain/search.py
Normal file
@ -0,0 +1,73 @@
|
||||
from typing import Optional, List
|
||||
|
||||
from app.chain import _ChainBase
|
||||
from app.chain.common import CommonChain
|
||||
from app.core import Context, MetaInfo, MediaInfo, TorrentInfo
|
||||
from app.core.meta import MetaBase
|
||||
from app.helper.sites import SitesHelper
|
||||
from app.log import logger
|
||||
|
||||
|
||||
class SearchChain(_ChainBase):
|
||||
"""
|
||||
站点资源搜索处理链
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.common = CommonChain()
|
||||
self.siteshelper = SitesHelper()
|
||||
|
||||
def process(self, meta: MetaBase, mediainfo: MediaInfo,
|
||||
keyword: str = None) -> Optional[List[Context]]:
|
||||
"""
|
||||
根据媒体信息,执行搜索
|
||||
:param meta: 元数据
|
||||
:param mediainfo: 媒体信息
|
||||
:param keyword: 搜索关键词
|
||||
"""
|
||||
# 执行搜索
|
||||
logger.info(f'开始搜索资源,关键词:{keyword or mediainfo.title} ...')
|
||||
torrents: List[TorrentInfo] = self.run_module(
|
||||
'search_torrents',
|
||||
mediainfo=mediainfo,
|
||||
keyword=keyword,
|
||||
sites=self.siteshelper.get_indexers()
|
||||
)
|
||||
if not torrents:
|
||||
logger.warn(f'{keyword or mediainfo.title} 未搜索到资源')
|
||||
return []
|
||||
# 过滤不匹配的资源
|
||||
_match_torrents = []
|
||||
if mediainfo:
|
||||
for torrent in torrents:
|
||||
# 比对IMDBID
|
||||
if torrent.imdbid \
|
||||
and mediainfo.imdb_id \
|
||||
and torrent.imdbid == mediainfo.imdb_id:
|
||||
_match_torrents.append(torrent)
|
||||
continue
|
||||
# 识别
|
||||
torrent_meta = MetaInfo(torrent.title, torrent.description)
|
||||
# 识别媒体信息
|
||||
torrent_mediainfo: MediaInfo = self.run_module('recognize_media', meta=torrent_meta)
|
||||
if not torrent_mediainfo:
|
||||
logger.warn(f'未识别到媒体信息,标题:{torrent.title}')
|
||||
continue
|
||||
# 过滤
|
||||
if torrent_mediainfo.tmdb_id == mediainfo.tmdb_id \
|
||||
and torrent_mediainfo.type == mediainfo.type:
|
||||
_match_torrents.append(torrent)
|
||||
else:
|
||||
_match_torrents = torrents
|
||||
# 过滤种子
|
||||
result = self.run_module("filter_torrents", torrent_list=_match_torrents)
|
||||
if result is not None:
|
||||
_match_torrents = result
|
||||
if not _match_torrents:
|
||||
logger.warn(f'{keyword or mediainfo.title} 没有符合过滤条件的资源')
|
||||
return []
|
||||
# 组装上下文返回
|
||||
return [Context(meta=MetaInfo(torrent.title),
|
||||
mediainfo=mediainfo,
|
||||
torrentinfo=torrent) for torrent in _match_torrents]
|
195
app/chain/subscribe.py
Normal file
195
app/chain/subscribe.py
Normal file
@ -0,0 +1,195 @@
|
||||
from typing import Dict, List
|
||||
|
||||
from app.chain import _ChainBase
|
||||
from app.chain.common import CommonChain
|
||||
from app.chain.search import SearchChain
|
||||
from app.core import MetaInfo, TorrentInfo, Context, MediaInfo
|
||||
from app.db.subscribes import Subscribes
|
||||
from app.helper.sites import SitesHelper
|
||||
from app.log import logger
|
||||
from app.utils.string import StringUtils
|
||||
from app.utils.types import MediaType
|
||||
|
||||
|
||||
class SubscribeChain(_ChainBase):
|
||||
"""
|
||||
订阅处理链
|
||||
"""
|
||||
|
||||
# 站点最新种子缓存 {站点域名: 种子上下文}
|
||||
_torrents_cache: Dict[str, List[Context]] = {}
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.common = CommonChain()
|
||||
self.searchchain = SearchChain()
|
||||
self.subscribes = Subscribes()
|
||||
self.siteshelper = SitesHelper()
|
||||
|
||||
def process(self, title: str,
|
||||
mtype: MediaType = None,
|
||||
tmdbid: str = None,
|
||||
season: int = None,
|
||||
username: str = None,
|
||||
**kwargs) -> bool:
|
||||
"""
|
||||
识别媒体信息并添加订阅
|
||||
"""
|
||||
logger.info(f'开始添加订阅,标题:{title} ...')
|
||||
# 识别前预处理
|
||||
result = self.run_module('prepare_recognize', title=title)
|
||||
if result:
|
||||
title, _ = result
|
||||
# 识别元数据
|
||||
metainfo = MetaInfo(title)
|
||||
if mtype:
|
||||
metainfo.type = mtype
|
||||
if season:
|
||||
metainfo.type = MediaType.TV
|
||||
metainfo.begin_season = season
|
||||
# 识别媒体信息
|
||||
mediainfo = self.run_module('recognize_media', meta=metainfo, tmdbid=tmdbid)
|
||||
if not mediainfo:
|
||||
logger.warn(f'未识别到媒体信息,标题:{title},tmdbid:{tmdbid}')
|
||||
return False
|
||||
# 更新媒体图片
|
||||
self.run_module('obtain_image', mediainfo=mediainfo)
|
||||
# 添加订阅
|
||||
state, err_msg = self.subscribes.add(mediainfo, season=season, **kwargs)
|
||||
if state:
|
||||
logger.info(f'{mediainfo.get_title_string()} {err_msg}')
|
||||
else:
|
||||
logger.error(f'{mediainfo.get_title_string()} 添加订阅成功')
|
||||
self.common.post_message(title=f"{mediainfo.get_title_string()} 已添加订阅",
|
||||
text="用户:{username}",
|
||||
image=mediainfo.get_message_image())
|
||||
# 返回结果
|
||||
return state
|
||||
|
||||
def search(self, sid: int = None, state: str = 'N'):
|
||||
"""
|
||||
订阅搜索
|
||||
:param sid: 订阅ID,有值时只处理该订阅
|
||||
:param state: 订阅状态 N:未搜索 R:已搜索
|
||||
:return: 更新订阅状态为R或删除订阅
|
||||
"""
|
||||
if sid:
|
||||
subscribes = [self.subscribes.get(sid)]
|
||||
else:
|
||||
subscribes = self.subscribes.list(state)
|
||||
# 遍历订阅
|
||||
for subscribe in subscribes:
|
||||
# 如果状态为N则更新为R
|
||||
if subscribe.state == 'N':
|
||||
self.subscribes.update(subscribe.id, {'state': 'R'})
|
||||
# 生成元数据
|
||||
meta = MetaInfo(subscribe.name)
|
||||
meta.year = subscribe.year
|
||||
meta.begin_season = subscribe.season
|
||||
meta.type = MediaType.MOVIE if subscribe.type == MediaType.MOVIE.value else MediaType.TV
|
||||
# 识别媒体信息
|
||||
mediainfo = self.run_module('recognize_media', meta=meta, tmdbid=subscribe.tmdbid)
|
||||
if not mediainfo:
|
||||
logger.warn(f'未识别到媒体信息,标题:{subscribe.name},tmdbid:{subscribe.tmdbid}')
|
||||
continue
|
||||
# 查询缺失的媒体信息
|
||||
exist_flag, no_exists = self.common.get_no_exists_info(mediainfo=mediainfo)
|
||||
if exist_flag:
|
||||
logger.info(f'{mediainfo.get_title_string()} 媒体库中已存在,完成订阅')
|
||||
self.subscribes.delete(subscribe.id)
|
||||
continue
|
||||
# 搜索
|
||||
contexts = self.searchchain.process(meta=meta, mediainfo=mediainfo, keyword=subscribe.keyword)
|
||||
if not contexts:
|
||||
logger.warn(f'{subscribe.keyword or subscribe.name} 未搜索到资源')
|
||||
continue
|
||||
# 自动下载
|
||||
_, lefts = self.common.batch_download(contexts=contexts, need_tvs=no_exists)
|
||||
if not lefts:
|
||||
# 全部下载完成
|
||||
logger.info(f'{mediainfo.get_title_string()} 下载完成,完成订阅')
|
||||
self.subscribes.delete(subscribe.id)
|
||||
else:
|
||||
# 未完成下载
|
||||
logger.info(f'{mediainfo.get_title_string()} 未下载未完整,继续订阅 ...')
|
||||
|
||||
def refresh(self):
|
||||
"""
|
||||
刷新站点最新资源
|
||||
"""
|
||||
# 所有站点索引
|
||||
indexers = self.siteshelper.get_indexers()
|
||||
# 遍历站点缓存资源
|
||||
for indexer in indexers:
|
||||
domain = StringUtils.get_url_domain(indexer.get("domain"))
|
||||
torrents: List[TorrentInfo] = self.run_module("refresh_torrents", sites=[indexer])
|
||||
if torrents:
|
||||
self._torrents_cache[domain] = []
|
||||
for torrent in torrents:
|
||||
# 识别
|
||||
meta = MetaInfo(torrent.title, torrent.description)
|
||||
# 识别媒体信息
|
||||
mediainfo = self.run_module('recognize_media', meta=meta)
|
||||
if not mediainfo:
|
||||
logger.warn(f'未识别到媒体信息,标题:{torrent.title}')
|
||||
continue
|
||||
# 上下文
|
||||
context = Context(meta=meta, mediainfo=mediainfo, torrentinfo=torrent)
|
||||
self._torrents_cache[domain].append(context)
|
||||
# 从缓存中匹配订阅
|
||||
self.match()
|
||||
|
||||
def match(self):
|
||||
"""
|
||||
从缓存中匹配订阅,并自动下载
|
||||
"""
|
||||
# 所有订阅
|
||||
subscribes = self.subscribes.list('R')
|
||||
# 遍历订阅
|
||||
for subscribe in subscribes:
|
||||
# 生成元数据
|
||||
meta = MetaInfo(subscribe.name)
|
||||
meta.year = subscribe.year
|
||||
meta.begin_season = subscribe.season
|
||||
meta.type = MediaType.MOVIE if subscribe.type == MediaType.MOVIE.value else MediaType.TV
|
||||
# 识别媒体信息
|
||||
mediainfo: MediaInfo = self.run_module('recognize_media', meta=meta, tmdbid=subscribe.tmdbid)
|
||||
if not mediainfo:
|
||||
logger.warn(f'未识别到媒体信息,标题:{subscribe.name},tmdbid:{subscribe.tmdbid}')
|
||||
continue
|
||||
# 查询缺失的媒体信息
|
||||
exist_flag, no_exists = self.common.get_no_exists_info(mediainfo=mediainfo)
|
||||
if exist_flag:
|
||||
logger.info(f'{mediainfo.get_title_string()} 媒体库中已存在,完成订阅')
|
||||
self.subscribes.delete(subscribe.id)
|
||||
continue
|
||||
# 遍历缓存种子
|
||||
_match_context = []
|
||||
for domain, contexts in self._torrents_cache.items():
|
||||
for context in contexts:
|
||||
# 检查是否匹配
|
||||
torrent_meta = context.meta_info
|
||||
torrent_mediainfo = context.media_info
|
||||
torrent_info = context.torrent_info
|
||||
if torrent_mediainfo.tmdb_id == mediainfo.tmdb_id \
|
||||
and torrent_mediainfo.type == mediainfo.type:
|
||||
if meta.begin_season and meta.begin_season != torrent_meta.begin_season:
|
||||
continue
|
||||
# 匹配成功
|
||||
logger.info(f'{mediainfo.get_title_string()} 匹配成功:{torrent_info.title}')
|
||||
_match_context.append(context)
|
||||
logger(f'{mediainfo.get_title_string()} 匹配完成,共匹配到{len(_match_context)}个资源')
|
||||
if _match_context:
|
||||
# 批量择优下载
|
||||
_, lefts = self.common.batch_download(contexts=_match_context, need_tvs=no_exists)
|
||||
if not lefts:
|
||||
# 全部下载完成
|
||||
logger.info(f'{mediainfo.get_title_string()} 下载完成,完成订阅')
|
||||
self.subscribes.delete(subscribe.id)
|
||||
else:
|
||||
# 未完成下载,计算剩余集数
|
||||
left_episodes = lefts.get(mediainfo.tmdb_id, {}).get("episodes", [])
|
||||
logger.info(f'{mediainfo.get_title_string()} 未下载未完整,更新缺失集数为{len(left_episodes)} ...')
|
||||
self.subscribes.update(subscribe.id, {
|
||||
"lack_episode": len(left_episodes)
|
||||
})
|
266
app/chain/user_message.py
Normal file
266
app/chain/user_message.py
Normal file
@ -0,0 +1,266 @@
|
||||
from typing import Dict
|
||||
|
||||
from fastapi import Request
|
||||
|
||||
from app.chain import _ChainBase
|
||||
from app.chain.common import *
|
||||
from app.chain.search import SearchChain
|
||||
from app.core import MediaInfo, TorrentInfo, MetaInfo
|
||||
from app.db.subscribes import Subscribes
|
||||
from app.log import logger
|
||||
from app.utils.types import EventType
|
||||
|
||||
|
||||
class UserMessageChain(_ChainBase):
|
||||
"""
|
||||
外来消息处理链
|
||||
"""
|
||||
# 缓存的用户数据 {userid: {type: str, items: list}}
|
||||
_user_cache: Dict[str, dict] = {}
|
||||
# 每页数据量
|
||||
_page_size: int = 8
|
||||
# 当前页面
|
||||
_current_page: int = 0
|
||||
# 当前元数据
|
||||
_current_meta: Optional[MetaInfo] = None
|
||||
# 当前媒体信息
|
||||
_current_media: Optional[MediaInfo] = None
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.common = CommonChain()
|
||||
self.subscribes = Subscribes()
|
||||
self.searchchain = SearchChain()
|
||||
|
||||
def process(self, request: Request, *args, **kwargs) -> None:
|
||||
"""
|
||||
识别消息内容,执行操作
|
||||
"""
|
||||
# 获取消息内容
|
||||
info: dict = self.run_module('message_parser', request=request)
|
||||
if not info:
|
||||
return
|
||||
# 用户ID
|
||||
userid = info.get('userid')
|
||||
if not userid:
|
||||
logger.debug(f'未识别到用户ID:{request}')
|
||||
return
|
||||
# 消息内容
|
||||
text = str(info.get('text')).strip() if info.get('text') else None
|
||||
if not text:
|
||||
logger.debug(f'未识别到消息内容:{request}')
|
||||
return
|
||||
logger.info(f'收到用户消息内容,用户:{userid},内容:{text}')
|
||||
if text.startswith('/'):
|
||||
# 执行命令
|
||||
self.eventmanager.send_event(
|
||||
EventType.CommandExcute,
|
||||
{
|
||||
"cmd": text
|
||||
}
|
||||
)
|
||||
elif text.isdigit():
|
||||
# 缓存
|
||||
cache_data: dict = self._user_cache.get(userid)
|
||||
# 选择项目
|
||||
if not cache_data \
|
||||
or not cache_data.get('items') \
|
||||
or len(cache_data.get('items')) < int(text):
|
||||
# 发送消息
|
||||
self.common.post_message(title="输入有误!", userid=userid)
|
||||
return
|
||||
# 缓存类型
|
||||
cache_type: str = cache_data.get('type')
|
||||
# 缓存列表
|
||||
cache_list: list = cache_data.get('items')
|
||||
# 选择
|
||||
if cache_type == "Search":
|
||||
mediainfo: MediaInfo = cache_list[int(text) - 1]
|
||||
self._current_media = mediainfo
|
||||
# 检查是否已存在
|
||||
exists: list = self.run_module('media_exists', mediainfo=mediainfo)
|
||||
if exists:
|
||||
# 已存在
|
||||
self.common.post_message(
|
||||
title=f"{mediainfo.type.value} {mediainfo.get_title_string()} 媒体库中已存在", userid=userid)
|
||||
return
|
||||
# 搜索种子
|
||||
contexts = self.searchchain.process(meta=self._current_meta, mediainfo=mediainfo)
|
||||
if not contexts:
|
||||
# 没有数据
|
||||
self.common.post_message(title=f"{mediainfo.title} 未搜索到资源!", userid=userid)
|
||||
return
|
||||
# 更新缓存
|
||||
self._user_cache[userid] = {
|
||||
"type": "Torrent",
|
||||
"items": contexts
|
||||
}
|
||||
self._current_page = 0
|
||||
# 发送种子数据
|
||||
self.__post_torrents_message(items=contexts[:self._page_size], userid=userid)
|
||||
|
||||
elif cache_type == "Subscribe":
|
||||
# 订阅媒体
|
||||
mediainfo: MediaInfo = cache_list[int(text) - 1]
|
||||
self._current_media = mediainfo
|
||||
state, msg = self.subscribes.add(mediainfo,
|
||||
season=self._current_meta.begin_season,
|
||||
episode=self._current_meta.begin_episode)
|
||||
if state:
|
||||
# 订阅成功
|
||||
self.common.post_message(
|
||||
title=f"{mediainfo.get_title_string()} 已添加订阅",
|
||||
image=mediainfo.get_message_image(),
|
||||
userid=userid)
|
||||
else:
|
||||
# 订阅失败
|
||||
self.common.post_message(title=f"{mediainfo.title} 添加订阅失败:{msg}", userid=userid)
|
||||
elif cache_type == "Torrent":
|
||||
if int(text) == 0:
|
||||
# 自动选择下载
|
||||
# 查询缺失的媒体信息
|
||||
exist_flag, no_exists = self.common.get_no_exists_info(mediainfo=self._current_media)
|
||||
if exist_flag:
|
||||
self.common.post_message(title=f"{self._current_media.get_title_string()} 媒体库中已存在",
|
||||
userid=userid)
|
||||
return
|
||||
# 批量下载
|
||||
self.common.batch_download(contexts=cache_list, need_tvs=no_exists, userid=userid)
|
||||
else:
|
||||
# 下载种子
|
||||
torrent: TorrentInfo = cache_list[int(text) - 1]
|
||||
# 识别种子信息
|
||||
meta = MetaInfo(torrent.title)
|
||||
# 预处理种子
|
||||
torrent_file, msg = self.run_module("prepare_torrent", torrentinfo=torrent)
|
||||
if not torrent_file:
|
||||
# 下载失败
|
||||
self.run_module('post_message',
|
||||
title=f"{torrent.title} 种子下载失败!",
|
||||
text=f"错误信息:{msg}\n种子链接:{torrent.enclosure}",
|
||||
userid=userid)
|
||||
return
|
||||
# 添加下载
|
||||
state, msg = self.run_module("download_torrent",
|
||||
torrent_path=torrent_file,
|
||||
mediainfo=self._current_media)
|
||||
if not state:
|
||||
# 下载失败
|
||||
self.common.post_message(title=f"{torrent.title} 添加下载失败!",
|
||||
text=f"错误信息:{msg}",
|
||||
userid=userid)
|
||||
return
|
||||
# 下载成功,发送通知
|
||||
self.common.post_download_message(meta=meta, mediainfo=self._current_media, torrent=torrent)
|
||||
|
||||
elif text.lower() == "p":
|
||||
# 上一页
|
||||
cache_data: dict = self._user_cache.get(userid)
|
||||
if not cache_data:
|
||||
# 没有缓存
|
||||
self.common.post_message(title="输入有误!", userid=userid)
|
||||
return
|
||||
|
||||
if self._current_page == 0:
|
||||
# 第一页
|
||||
self.common.post_message(title="已经是第一页了!", userid=userid)
|
||||
return
|
||||
cache_type: str = cache_data.get('type')
|
||||
cache_list: list = cache_data.get('items')
|
||||
# 减一页
|
||||
self._current_page -= 1
|
||||
if self._current_page == 0:
|
||||
start = 0
|
||||
end = self._page_size
|
||||
else:
|
||||
start = self._current_page * self._page_size
|
||||
end = start + self._page_size
|
||||
if cache_type == "Torrent":
|
||||
# 发送种子数据
|
||||
self.__post_torrents_message(items=cache_list[start:end], userid=userid)
|
||||
else:
|
||||
# 发送媒体数据
|
||||
self.__post_medias_message(items=cache_list[start:end], userid=userid)
|
||||
|
||||
elif text.lower() == "n":
|
||||
# 下一页
|
||||
cache_data: dict = self._user_cache.get(userid)
|
||||
if not cache_data:
|
||||
# 没有缓存
|
||||
self.common.post_message(title="输入有误!", userid=userid)
|
||||
return
|
||||
cache_type: str = cache_data.get('type')
|
||||
cache_list: list = cache_data.get('items')
|
||||
# 加一页
|
||||
self._current_page += 1
|
||||
cache_list = cache_list[self._current_page * self._page_size:]
|
||||
if not cache_list:
|
||||
# 没有数据
|
||||
self.common.post_message(title="已经是最后一页了!", userid=userid)
|
||||
return
|
||||
else:
|
||||
if cache_type == "Torrent":
|
||||
# 发送种子数据
|
||||
self.__post_torrents_message(items=cache_list, userid=userid)
|
||||
else:
|
||||
# 发送媒体数据
|
||||
self.__post_medias_message(items=cache_list, userid=userid)
|
||||
|
||||
else:
|
||||
# 搜索或订阅
|
||||
if text.startswith("订阅"):
|
||||
# 订阅
|
||||
content = re.sub(r"订阅[::\s]*", "", text)
|
||||
action = "Subscribe"
|
||||
else:
|
||||
# 搜索
|
||||
content = re.sub(r"(搜索|下载)[::\s]*", "", text)
|
||||
action = "Search"
|
||||
# 提取要素
|
||||
mtype, key_word, season_num, episode_num, year, title = StringUtils.get_keyword(content)
|
||||
# 识别
|
||||
meta = MetaInfo(title)
|
||||
if not meta.get_name():
|
||||
self.common.post_message(title="无法识别输入内容!", userid=userid)
|
||||
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
|
||||
# 开始搜索
|
||||
medias = self.run_module('search_medias', meta=meta)
|
||||
if not medias:
|
||||
self.common.post_message(title=f"{meta.get_name()} 没有找到对应的媒体信息!", userid=userid)
|
||||
return
|
||||
self._user_cache[userid] = {
|
||||
'type': action,
|
||||
'items': medias
|
||||
}
|
||||
self._current_page = 0
|
||||
self._current_media = None
|
||||
# 发送媒体列表
|
||||
self.__post_medias_message(items=medias[:self._page_size], userid=userid)
|
||||
|
||||
def __post_medias_message(self, items: list, userid: str):
|
||||
"""
|
||||
发送媒体列表消息
|
||||
"""
|
||||
self.run_module('post_medias_message',
|
||||
title="请回复数字选择对应媒体(p:上一页, n:下一页)",
|
||||
items=items,
|
||||
userid=userid)
|
||||
|
||||
def __post_torrents_message(self, items: list, userid: str):
|
||||
"""
|
||||
发送种子列表消息
|
||||
"""
|
||||
self.run_module('post_torrents_message',
|
||||
title="请回复数字下载对应资源(0:自动选择, p:上一页, n:下一页)",
|
||||
items=items,
|
||||
userid=userid)
|
20
app/chain/webhook_message.py
Normal file
20
app/chain/webhook_message.py
Normal file
@ -0,0 +1,20 @@
|
||||
from typing import Any
|
||||
|
||||
from app.chain import _ChainBase
|
||||
|
||||
|
||||
class WebhookMessageChain(_ChainBase):
|
||||
"""
|
||||
响应Webhook事件
|
||||
"""
|
||||
|
||||
def process(self, message: dict) -> None:
|
||||
"""
|
||||
处理Webhook报文并发送消息
|
||||
"""
|
||||
# 获取主体内容
|
||||
info = self.run_module('webhook_parser', message=message)
|
||||
if not info:
|
||||
return
|
||||
# 发送消息
|
||||
self.run_module("post_message", title=info.get("title"), text=info.get("text"), image=info.get("image"))
|
Reference in New Issue
Block a user