diff --git a/app/brushtask.py b/app/brushtask.py index 249964e6..64981335 100644 --- a/app/brushtask.py +++ b/app/brushtask.py @@ -847,40 +847,49 @@ def __check_remove_rule(remove_rule, @staticmethod def __get_torrent_dict(downloader_type, torrent): - # 当前时间 - date_now = int(time.mktime(datetime.now().timetuple())) + + # 当前时间戳 + date_now = int(time.time()) if downloader_type == "qbittorrent": # ID torrent_id = torrent.get("hash") - # 已开始时间 秒 - dltime = int(time.time() - torrent.get("added_on")) - # 已做种时间 秒 - date_done = torrent.completion_on if torrent.completion_on > 0 else torrent.added_on - seeding_time = date_now - date_done if date_done else 0 + # 下载时间 + dltime = date_now - torrent.get("added_on") if torrent.get("added_on") else 0 + # 做种时间 + seeding_time = date_now - torrent.get("completion_on") if torrent.get("completion_on") else 0 # 分享率 ratio = torrent.get("ratio") or 0 # 上传量 uploaded = torrent.get("uploaded") or 0 # 平均上传速度 Byte/s - avg_upspeed = int(uploaded / dltime) + if dltime: + avg_upspeed = int(uploaded / dltime) + else: + avg_upspeed = uploaded # 已未活动 秒 - last_activity = int(torrent.get("last_activity", 0)) - iatime = date_now - last_activity if last_activity else 0 + iatime = date_now - torrent.get("last_activity") if torrent.get("last_activity") else 0 # 下载量 downloaded = torrent.get("downloaded") # 种子大小 total_size = torrent.get("total_size") # 添加时间 - add_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(torrent.get("added_on"))) + add_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(torrent.get("added_on") or 0)) + else: + # ID torrent_id = torrent.hashString # 做种时间 - date_done = torrent.date_done or torrent.date_added + if not torrent.date_done or torrent.date_done.timestamp() < 1: + seeding_time = 0 + else: + seeding_time = date_now - int(torrent.date_done.timestamp()) # 下载耗时 - dltime = date_now - int(time.mktime(torrent.date_added.timetuple())) - seeding_time = date_now - int(time.mktime(date_done.timetuple())) + if not torrent.date_added or torrent.date_added.timestamp() < 1: + dltime = 0 + else: + dltime = date_now - int(torrent.date_added.timestamp()) # 下载量 downloaded = int(torrent.total_size * torrent.progress / 100) # 分享率 @@ -888,13 +897,20 @@ def __get_torrent_dict(downloader_type, torrent): # 上传量 uploaded = int(downloaded * torrent.ratio) # 平均上传速度 - avg_upspeed = int(uploaded / dltime) + if dltime: + avg_upspeed = int(uploaded / dltime) + else: + avg_upspeed = uploaded # 未活动时间 - iatime = date_now - int(time.mktime(torrent.date_active.timetuple())) + if not torrent.date_active or torrent.date_active.timestamp() < 1: + iatime = 0 + else: + iatime = date_now - int(torrent.date_active.timestamp()) # 种子大小 total_size = torrent.total_size # 添加时间 - add_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(torrent.date_added)) + add_time = time.strftime('%Y-%m-%d %H:%M:%S', + time.localtime(torrent.date_added.timestamp() if torrent.date_added else 0)) return { "id": torrent_id, diff --git a/app/downloader/client/transmission.py b/app/downloader/client/transmission.py index e08e8fcd..d51bc018 100644 --- a/app/downloader/client/transmission.py +++ b/app/downloader/client/transmission.py @@ -19,7 +19,7 @@ class Transmission(_IDownloadClient): # 下载器名称 client_name = DownloaderType.TR.value - # 参考transmission web,仅查询需要的参数,加速种子检索 + # 参考transmission web,仅查询需要的参数,加速种子搜索 _trarg = ["id", "name", "status", "labels", "hashString", "totalSize", "percentDone", "addedDate", "trackerStats", "leftUntilDone", "rateDownload", "rateUpload", "recheckProgress", "rateDownload", "rateUpload", "peersGettingFromUs", "peersSendingToUs", "uploadRatio", "uploadedEver", "downloadedEver", "downloadDir", diff --git a/app/downloader/downloader.py b/app/downloader/downloader.py index f394ce20..0f718d1c 100644 --- a/app/downloader/downloader.py +++ b/app/downloader/downloader.py @@ -935,7 +935,7 @@ def check_exists_medias(self, meta_info, no_exists=None, total_ep=None): """ 检查媒体库,查询是否存在,对于剧集同时返回不存在的季集信息 :param meta_info: 已识别的媒体信息,包括标题、年份、季、集信息 - :param no_exists: 在调用该方法前已经存储的不存在的季集信息,有传入时该函数检索的内容将会叠加后输出 + :param no_exists: 在调用该方法前已经存储的不存在的季集信息,有传入时该函数搜索的内容将会叠加后输出 :param total_ep: 各季的总集数 :return: 当前媒体是否缺失,各标题总的季集和缺失的季集,需要发送的消息 """ @@ -958,7 +958,7 @@ def check_exists_medias(self, meta_info, no_exists=None, total_ep=None): if meta_info.type != MediaType.MOVIE: # 是否存在的标志 return_flag = False - # 检索电视剧的信息 + # 搜索电视剧的信息 tv_info = self.media.get_tmdb_info(mtype=MediaType.TV, tmdbid=meta_info.tmdb_id) if tv_info: # 传入检查季 diff --git a/app/filetransfer.py b/app/filetransfer.py index 09fc5d8e..62a1b43b 100644 --- a/app/filetransfer.py +++ b/app/filetransfer.py @@ -434,7 +434,7 @@ def transfer_media(self, 识别并转移一个文件、多个文件或者目录 :param in_from: 来源,即调用该功能的渠道 :param in_path: 转移的路径,可能是一个文件也可以是一个目录 - :param files: 文件清单,非空时以该文件清单为准,为空时从in_path中按后缀和大小限制检索需要处理的文件清单 + :param files: 文件清单,非空时以该文件清单为准,为空时从in_path中按后缀和大小限制搜索需要处理的文件清单 :param target_dir: 目的文件夹,非空的转移到该文件夹,为空时则按类型转移到配置文件中的媒体库文件夹 :param unknown_dir: 未识别文件夹,非空时未识别的媒体文件转移到该文件夹,为空时则使用配置文件中的未识别文件夹 :param rmt_mode: 文件转移方式 @@ -559,11 +559,11 @@ def __finish_transfer(status, message): log.info("【Rmt】所有文件均已成功转移过,没有需要处理的文件!如需重新处理,请清理缓存(服务->清理转移缓存)") return __finish_transfer(True, "没有新文件需要处理") - # API检索出媒体信息,传入一个文件列表,得出每一个文件的名称,这里是当前目录下所有的文件了 + # API搜索出媒体信息,传入一个文件列表,得出每一个文件的名称,这里是当前目录下所有的文件了 Medias = self.media.get_media_info_on_files(file_list, tmdb_info, media_type, season, episode[0]) if not Medias: - log.error("【Rmt】检索媒体信息出错!") - return __finish_transfer(False, "检索媒体信息出错") + log.error("【Rmt】搜索媒体信息出错!") + return __finish_transfer(False, "搜索媒体信息出错") # 更新进度 self.progress.update(ptype=ProgressKey.FileTransfer, text=f"共 {len(Medias)} 个文件需要处理...") diff --git a/app/helper/db_helper.py b/app/helper/db_helper.py index 8db4af82..a9883663 100644 --- a/app/helper/db_helper.py +++ b/app/helper/db_helper.py @@ -67,13 +67,13 @@ def insert_search_results(self, media_items: list, title=None, ident_flag=True): def get_search_result_by_id(self, dl_id): """ - 根据ID从数据库中查询检索结果的一条记录 + 根据ID从数据库中查询搜索结果的一条记录 """ return self._db.query(SEARCHRESULTINFO).filter(SEARCHRESULTINFO.ID == dl_id).all() def get_search_results(self, ): """ - 查询检索结果的所有记录 + 查询搜索结果的所有记录 """ return self._db.query(SEARCHRESULTINFO).all() @@ -193,7 +193,7 @@ def update_douban_media_state(self, media, state): def get_douban_search_state(self, title, year=None): """ - 查询未检索的豆瓣数据 + 查询未搜索的豆瓣数据 """ if not year: return self._db.query(DOUBANMEDIAS.STATE).filter(DOUBANMEDIAS.NAME == title).first() diff --git a/app/helper/meta_helper.py b/app/helper/meta_helper.py index 0385703e..0cd1f341 100644 --- a/app/helper/meta_helper.py +++ b/app/helper/meta_helper.py @@ -71,7 +71,7 @@ def get_meta_data_by_key(self, key): def dump_meta_data(self, search, page, num): """ 分页获取当前缓存列表 - @param search: 检索的缓存key + @param search: 搜索的缓存key @param page: 页码 @param num: 单页大小 @return: 总数, 缓存列表 @@ -113,7 +113,7 @@ def delete_meta_data_by_tmdbid(self, tmdbid): def delete_unknown_meta(self): """ - 清除未识别的缓存记录,以便重新检索TMDB + 清除未识别的缓存记录,以便重新搜索TMDB """ for key in list(self._meta_data): if str(self._meta_data.get(key, {}).get("id")) == '0': diff --git a/app/helper/ocr_helper.py b/app/helper/ocr_helper.py index 04dfa2ff..eac95c86 100644 --- a/app/helper/ocr_helper.py +++ b/app/helper/ocr_helper.py @@ -5,27 +5,30 @@ class OcrHelper: - req = None - _ocr_b64_url = "%s/captcha/base64" % DEFAULT_OCR_SERVER - def __init__(self): - self.req = RequestUtils(content_type="application/json") + _ocr_b64_url = "%s/captcha/base64" % DEFAULT_OCR_SERVER - def get_captcha_text(self, image_url=None, image_b64=None): + def get_captcha_text(self, image_url=None, image_b64=None, cookie=None, ua=None): """ 根据图片地址,获取验证码图片,并识别内容 + :param image_url: 图片地址 + :param image_b64: 图片base64,跳过图片地址下载 + :param cookie: 下载图片使用的cookie + :param ua: 下载图片使用的ua """ - if not image_url and not image_b64: - return "" if image_url: - ret = self.req.get_res(image_url) + ret = RequestUtils(headers=ua, + cookies=cookie).get_res(image_url) if ret is not None: image_bin = ret.content if not image_bin: return "" image_b64 = base64.b64encode(image_bin).decode() - ret = self.req.post_res(url=self._ocr_b64_url, - json={"base64_img": image_b64}) + if not image_b64: + return "" + ret = RequestUtils(content_type="application/json").post_res( + url=self._ocr_b64_url, + json={"base64_img": image_b64}) if ret: return ret.json().get("result") return "" diff --git a/app/indexer/client/_base.py b/app/indexer/client/_base.py index 56e198da..c691ea10 100644 --- a/app/indexer/client/_base.py +++ b/app/indexer/client/_base.py @@ -73,7 +73,7 @@ def search(self, order_seq, match_media, in_from: SearchType): """ - 根据关键字多线程检索 + 根据关键字多线程搜索 """ if not indexer or not key_word: return None @@ -84,7 +84,7 @@ def search(self, order_seq, return [] # 计算耗时 start_time = datetime.datetime.now() - log.info(f"【{self.index_type}】开始检索Indexer:{indexer.name} ...") + log.info(f"【{self.index_type}】开始搜索Indexer:{indexer.name} ...") # 特殊符号处理 search_word = StringUtils.handler_special_chars(text=key_word, replace_word=" ", @@ -95,8 +95,8 @@ def search(self, order_seq, # 索引花费时间 seconds = (datetime.datetime.now() - start_time).seconds if len(result_array) == 0: - log.warn(f"【{self.index_type}】{indexer.name} 未检索到数据") - self.progress.update(ptype=ProgressKey.Search, text=f"{indexer.name} 未检索到数据") + log.warn(f"【{self.index_type}】{indexer.name} 未搜索到数据") + self.progress.update(ptype=ProgressKey.Search, text=f"{indexer.name} 未搜索到数据") self.dbhelper.insert_indexer_statistics(indexer=indexer.name, itype=self.client_id, @@ -231,7 +231,7 @@ def filter_search_results(self, result_array: list, match_media, start_time): """ - 从检索结果中匹配符合资源条件的记录 + 从搜索结果中匹配符合资源条件的记录 """ ret_array = [] index_sucess = 0 diff --git a/app/indexer/client/_spider.py b/app/indexer/client/_spider.py index 417c0b63..b6b3fc8e 100644 --- a/app/indexer/client/_spider.py +++ b/app/indexer/client/_spider.py @@ -45,7 +45,7 @@ class TorrentSpider(feapder.AirSpider): custom_argument=["--ignore-certificate-errors"], ) ) - # 是否检索完成标志 + # 是否搜索完成标志 is_complete = False # 是否出现错误 is_error = False @@ -65,13 +65,13 @@ class TorrentSpider(feapder.AirSpider): render = False # Referer referer = None - # 检索关键字 + # 搜索关键字 keyword = None # 媒体类型 mtype = None - # 检索路径、方式配置 + # 搜索路径、方式配置 search = {} - # 批量检索配置 + # 批量搜索配置 batch = {} # 浏览配置 browse = {} @@ -83,7 +83,7 @@ class TorrentSpider(feapder.AirSpider): fields = {} # 页码 page = 0 - # 检索条数 + # 搜索条数 result_num = 100 # 单个种子信息 torrents_info = {} @@ -98,7 +98,7 @@ def setparam(self, indexer, """ 设置查询参数 :param indexer: 索引器 - :param keyword: 检索关键字,如果数组则为批量检索 + :param keyword: 搜索关键字,如果数组则为批量搜索 :param page: 页码 :param referer: Referer :param mtype: 媒体类型 @@ -181,7 +181,7 @@ def start_requests(self): # 查询模式与 search_mode = "0" - # 检索URL + # 搜索URL if self.search.get("params"): # 变量字典 inputs_dict = { @@ -243,7 +243,7 @@ def start_requests(self): }) elif self.page: torrentspath = torrentspath + f"?page={self.page}" - # 检索Url + # 搜索Url searchurl = self.domain + str(torrentspath).format(**inputs_dict) log.info(f"【Spider】开始请求:{searchurl}") @@ -623,7 +623,7 @@ def Getinfo(self, torrent): self.Getlabels(torrent) except Exception as err: ExceptionUtils.exception_traceback(err) - log.error("【Spider】%s 检索出现错误:%s" % (self.indexername, str(err))) + log.error("【Spider】%s 搜索出现错误:%s" % (self.indexername, str(err))) return self.torrents_info @staticmethod diff --git a/app/indexer/client/builtin.py b/app/indexer/client/builtin.py index fd71ea69..b4b2c002 100644 --- a/app/indexer/client/builtin.py +++ b/app/indexer/client/builtin.py @@ -97,7 +97,7 @@ def search(self, order_seq, match_media, in_from: SearchType): """ - 根据关键字多线程检索 + 根据关键字多线程搜索 """ if not indexer or not key_word: return None @@ -123,7 +123,7 @@ def search(self, order_seq, # 计算耗时 start_time = datetime.datetime.now() - log.info(f"【{self.client_name}】开始检索Indexer:{indexer.name} ...") + log.info(f"【{self.client_name}】开始搜索Indexer:{indexer.name} ...") # 特殊符号处理 search_word = StringUtils.handler_special_chars(text=key_word, replace_word=" ", @@ -160,9 +160,9 @@ def search(self, order_seq, result='N' if error_flag else 'Y') # 返回结果 if len(result_array) == 0: - log.warn(f"【{self.client_name}】{indexer.name} 未检索到数据") + log.warn(f"【{self.client_name}】{indexer.name} 未搜索到数据") # 更新进度 - self.progress.update(ptype=ProgressKey.Search, text=f"{indexer.name} 未检索到数据") + self.progress.update(ptype=ProgressKey.Search, text=f"{indexer.name} 未搜索到数据") return [] else: log.warn(f"【{self.client_name}】{indexer.name} 返回数据:{len(result_array)}") @@ -178,7 +178,7 @@ def search(self, order_seq, def list(self, index_id, page=0, keyword=None): """ - 根据站点ID检索站点首页资源 + 根据站点ID搜索站点首页资源 """ if not index_id: return [] diff --git a/app/indexer/indexer.py b/app/indexer/indexer.py index c56e425a..dc59cd6a 100644 --- a/app/indexer/indexer.py +++ b/app/indexer/indexer.py @@ -118,8 +118,8 @@ def search_by_keyword(self, match_media=None, in_from: SearchType = None): """ - 根据关键字调用 Index API 检索 - :param key_word: 检索的关键字,不能为空 + 根据关键字调用 Index API 搜索 + :param key_word: 搜索的关键字,不能为空 :param filter_args: 过滤条件,对应属性为空则不过滤,{"season":季, "episode":集, "year":年, "type":类型, "site":站点, "":, "restype":质量, "pix":分辨率, "sp_state":促销状态, "key":其它关键字} sp_state: 为UL DL,* 代表不关心, @@ -137,13 +137,13 @@ def search_by_keyword(self, # 计算耗时 start_time = datetime.datetime.now() if filter_args and filter_args.get("site"): - log.info(f"【{self._client_type.value}】开始检索 %s,站点:%s ..." % (key_word, filter_args.get("site"))) + log.info(f"【{self._client_type.value}】开始搜索 %s,站点:%s ..." % (key_word, filter_args.get("site"))) self.progress.update(ptype=ProgressKey.Search, - text="开始检索 %s,站点:%s ..." % (key_word, filter_args.get("site"))) + text="开始搜索 %s,站点:%s ..." % (key_word, filter_args.get("site"))) else: - log.info(f"【{self._client_type.value}】开始并行检索 %s,线程数:%s ..." % (key_word, len(indexers))) + log.info(f"【{self._client_type.value}】开始并行搜索 %s,线程数:%s ..." % (key_word, len(indexers))) self.progress.update(ptype=ProgressKey.Search, - text="开始并行检索 %s,线程数:%s ..." % (key_word, len(indexers))) + text="开始并行搜索 %s,线程数:%s ..." % (key_word, len(indexers))) # 多线程 executor = ThreadPoolExecutor(max_workers=len(indexers)) all_task = [] @@ -168,10 +168,10 @@ def search_by_keyword(self, ret_array = ret_array + result # 计算耗时 end_time = datetime.datetime.now() - log.info(f"【{self._client_type.value}】所有站点检索完成,有效资源数:%s,总耗时 %s 秒" + log.info(f"【{self._client_type.value}】所有站点搜索完成,有效资源数:%s,总耗时 %s 秒" % (len(ret_array), (end_time - start_time).seconds)) self.progress.update(ptype=ProgressKey.Search, - text="所有站点检索完成,有效资源数:%s,总耗时 %s 秒" + text="所有站点搜索完成,有效资源数:%s,总耗时 %s 秒" % (len(ret_array), (end_time - start_time).seconds), value=100) return ret_array diff --git a/app/media/media.py b/app/media/media.py index f6972974..736642bc 100644 --- a/app/media/media.py +++ b/app/media/media.py @@ -128,7 +128,7 @@ def __compare_tmdb_names(file_name, tmdb_names): def __search_tmdb_allnames(self, mtype: MediaType, tmdb_id): """ - 检索tmdb中所有的标题和译名,用于名称匹配 + 搜索tmdb中所有的标题和译名,用于名称匹配 :param mtype: 类型:电影、电视剧、动漫 :param tmdb_id: TMDB的ID :return: 所有译名的清单 @@ -169,7 +169,7 @@ def __search_tmdb(self, file_media_name, media_year=None, season_number=None): """ - 检索tmdb中的媒体信息,匹配返回一条尽可能正确的信息 + 搜索tmdb中的媒体信息,匹配返回一条尽可能正确的信息 :param file_media_name: 剑索的名称 :param search_type: 类型:电影、电视剧、动漫 :param first_media_year: 年份,如要是季集需要是首播年份(first_air_date) @@ -181,7 +181,7 @@ def __search_tmdb(self, file_media_name, return None if not file_media_name: return None - # TMDB检索 + # TMDB搜索 info = {} if search_type == MediaType.MOVIE: year_range = [first_media_year] @@ -492,7 +492,7 @@ def __failed_none(): @lru_cache(maxsize=512) def __search_tmdb_web(self, file_media_name, mtype: MediaType): """ - 检索TMDB网站,直接抓取结果,结果只有一条时才返回 + 搜索TMDB网站,直接抓取结果,结果只有一条时才返回 :param file_media_name: 名称 """ if not file_media_name: @@ -717,7 +717,7 @@ def get_media_info(self, title, :param strict: 是否严格模式,为true时,不会再去掉年份再查一次 :param cache: 是否使用缓存,默认TRUE :param language: 语言 - :param chinese: 原标题为英文时是否从别名中检索中文名称 + :param chinese: 原标题为英文时是否从别名中搜索中文名称 :param append_to_response: 额外查询的信息 :return: 带有TMDB信息的MetaInfo对象 """ @@ -859,12 +859,12 @@ def get_media_info_on_files(self, """ 根据文件清单,搜刮TMDB信息,用于文件名称的识别 :param file_list: 文件清单,如果是列表也可以是单个文件,也可以是一个目录 - :param tmdb_info: 如有传入TMDB信息则以该TMDB信息赋于所有文件,否则按名称从TMDB检索,用于手工识别时传入 - :param media_type: 媒体类型:电影、电视剧、动漫,如有传入以该类型赋于所有文件,否则按名称从TMDB检索并识别 + :param tmdb_info: 如有传入TMDB信息则以该TMDB信息赋于所有文件,否则按名称从TMDB搜索,用于手工识别时传入 + :param media_type: 媒体类型:电影、电视剧、动漫,如有传入以该类型赋于所有文件,否则按名称从TMDB搜索并识别 :param season: 季号,如有传入以该季号赋于所有文件,否则从名称中识别 :param episode_format: EpisodeFormat :param language: 语言 - :param chinese: 原标题为英文时是否从别名中检索中文名称 + :param chinese: 原标题为英文时是否从别名中搜索中文名称 :param append_to_response: 附加信息 :return: 带有TMDB信息的每个文件对应的MetaInfo对象字典 """ diff --git a/app/plugins/modules/doubansync.py b/app/plugins/modules/doubansync.py index e876ffa4..3a0bf8d9 100644 --- a/app/plugins/modules/doubansync.py +++ b/app/plugins/modules/doubansync.py @@ -377,7 +377,7 @@ def sync(self, event=None): self.info("开始同步豆瓣数据...") # 拉取豆瓣数据 medias = self.__get_all_douban_movies() - # 开始检索 + # 开始搜索 for media in medias: if not media or not media.get_name(): continue @@ -386,7 +386,7 @@ def sync(self, event=None): search_state = self.dbhelper.get_douban_search_state(media.get_name(), media.year) if not search_state or search_state[0] == "NEW": if self._auto_search: - # 需要检索 + # 需要搜索 if media.begin_season: subtitle = "第%s季" % media.begin_season else: @@ -409,7 +409,7 @@ def sync(self, event=None): if not self._auto_rss: # 合并季 media_info.begin_season = media.begin_season - # 开始检索 + # 开始搜索 search_result, no_exists, search_count, download_count = self.searcher.search_one_media( media_info=media_info, in_from=SearchType.DB, @@ -419,7 +419,7 @@ def sync(self, event=None): # 下载全了更新为已下载,没下载全的下次同步再次搜索 self.dbhelper.insert_douban_media_state(media, "DOWNLOADED") else: - # 需要加订阅,则由订阅去检索 + # 需要加订阅,则由订阅去搜索 self.info( "%s %s 更新到%s订阅中..." % (media.get_name(), media.year, media.type.value)) code, msg, _ = self.subscribe.add_rss_subscribe(mtype=media.type, @@ -438,7 +438,7 @@ def sync(self, event=None): # 插入为已RSS状态 self.dbhelper.insert_douban_media_state(media, "RSS") else: - # 不需要检索 + # 不需要搜索 if self._auto_rss: # 加入订阅,使状态为R self.info("%s %s 更新到%s订阅中..." % ( @@ -474,7 +474,7 @@ def sync(self, event=None): def __get_all_douban_movies(self): """ 获取每一个用户的每一个类型的豆瓣标记 - :return: 检索到的媒体信息列表(不含TMDB信息) + :return: 搜索到的媒体信息列表(不含TMDB信息) """ if not self._interval \ or not self._users \ diff --git a/app/plugins/modules/opensubtitles.py b/app/plugins/modules/opensubtitles.py index 8df2846f..a67c44a1 100644 --- a/app/plugins/modules/opensubtitles.py +++ b/app/plugins/modules/opensubtitles.py @@ -96,7 +96,7 @@ def download(self, event): # 媒体信息 item_media = item.get("media_info") if item_media.get("type") != MediaType.MOVIE.value and not item_media.get("imdb_id"): - self.warn("电视剧类型需要imdbid才能检索字幕!") + self.warn("电视剧类型需要imdbid才能搜索字幕!") return # 查询名称 item_name = item_media.get("en_name") or item_media.get("cn_name") @@ -113,10 +113,10 @@ def download(self, event): # 后缀 item_file_ext = item.get("file_ext") - self.info("开始从Opensubtitle.org检索字幕: %s,imdbid=%s" % (item_name, imdb_id)) + self.info("开始从Opensubtitle.org搜索字幕: %s,imdbid=%s" % (item_name, imdb_id)) subtitles = self.search_subtitles(imdb_id=imdb_id, name=item_name, year=item_year) if not subtitles: - self.warn("%s 未检索到字幕" % item_name) + self.warn("%s 未搜索到字幕" % item_name) else: self.info("opensubtitles.org返回数据:%s" % len(subtitles)) # 成功数 diff --git a/app/rss.py b/app/rss.py index edc7484c..2fc13a66 100644 --- a/app/rss.py +++ b/app/rss.py @@ -44,7 +44,7 @@ def init_config(self): def rssdownload(self): """ - RSS订阅检索下载入口,由定时服务调用 + RSS订阅搜索下载入口,由定时服务调用 """ if not self._sites: @@ -104,7 +104,7 @@ def rssdownload(self): continue # 站点名称 site_name = site_info.get("name") - # 没有订阅的站点中的不检索 + # 没有订阅的站点中的不搜索 if check_sites and site_name not in check_sites: continue # 站点rss链接 @@ -152,7 +152,7 @@ def rssdownload(self): if self.dbhelper.is_torrent_rssd(enclosure): log.info(f"【Rss】{title} 已成功订阅过") continue - # 识别种子名称,开始检索TMDB + # 识别种子名称,开始搜索TMDB media_info = MetaInfo(title=title) cache_info = self.media.get_cache_info(media_info) if cache_info.get("id"): diff --git a/app/rsschecker.py b/app/rsschecker.py index 5a5eb937..a4cbec20 100644 --- a/app/rsschecker.py +++ b/app/rsschecker.py @@ -231,7 +231,7 @@ def check_task_rss(self, taskid): continue if task_type == "D": - # 识别种子名称,开始检索TMDB + # 识别种子名称,开始搜索TMDB media_info = MetaInfo(title=meta_name, mtype=mediatype) cache_info = self.media.get_cache_info(media_info) @@ -314,7 +314,7 @@ def check_task_rss(self, taskid): rss_download_torrents.append(media_info) res_num = res_num + 1 elif task_type == "R": - # 识别种子名称,开始检索TMDB + # 识别种子名称,开始搜索TMDB media_info = MetaInfo(title=meta_name, mtype=mediatype) # 检查种子是否匹配过滤条件 filter_args = { @@ -578,7 +578,7 @@ def test_rss_articles(self, taskid, title): taskinfo = self.get_rsstask_info(taskid) if not taskinfo: return - # 识别种子名称,开始检索TMDB + # 识别种子名称,开始搜索TMDB media_info = MetaInfo(title=title) cache_info = self.media.get_cache_info(media_info) if cache_info.get("id"): diff --git a/app/scheduler.py b/app/scheduler.py index 52f9369d..cda5719e 100644 --- a/app/scheduler.py +++ b/app/scheduler.py @@ -77,7 +77,7 @@ def run_service(self): self.SCHEDULER.add_job(Rss().rssdownload, 'interval', seconds=pt_check_interval) log.info("RSS订阅服务启动") - # RSS订阅定时检索 + # RSS订阅定时搜索 search_rss_interval = self._pt.get('search_rss_interval') if search_rss_interval: if isinstance(search_rss_interval, str) and search_rss_interval.isdigit(): @@ -117,7 +117,7 @@ def run_service(self): # 定时把队列中的监控文件转移走 self.SCHEDULER.add_job(Sync().transfer_mon_files, 'interval', seconds=SYNC_TRANSFER_INTERVAL) - # RSS队列中检索 + # RSS队列中搜索 self.SCHEDULER.add_job(Subscribe().subscribe_search, 'interval', seconds=RSS_CHECK_INTERVAL) # 豆瓣RSS转TMDB,定时更新TMDB数据 diff --git a/app/searcher.py b/app/searcher.py index 6d0b3606..cbdbf205 100644 --- a/app/searcher.py +++ b/app/searcher.py @@ -41,7 +41,7 @@ def search_medias(self, in_from: SearchType = None): """ 根据关键字调用索引器检查媒体 - :param key_word: 检索的关键字,不能为空 + :param key_word: 搜索的关键字,不能为空 :param filter_args: 过滤条件 :param match_media: 区配的媒体信息 :param in_from: 搜索渠道 @@ -70,11 +70,11 @@ def search_one_media(self, media_info, filters: dict = None, user_name=None): """ - 只检索和下载一个资源,用于精确检索下载,由微信、Telegram或豆瓣调用 + 只搜索和下载一个资源,用于精确搜索下载,由微信、Telegram或豆瓣调用 :param media_info: 已识别的媒体信息 :param in_from: 搜索渠道 :param no_exists: 缺失的剧集清单 - :param sites: 检索哪些站点 + :param sites: 搜索哪些站点 :param filters: 过滤条件,为空则不过滤 :param user_name: 用户名 :return: 请求的资源是否全部下载完整,如完整则返回媒体信息 @@ -139,7 +139,7 @@ def search_one_media(self, media_info, if search_en_name: second_search_name = search_en_name # 开始搜索 - log.info("【Searcher】开始检索 %s ..." % first_search_name) + log.info("【Searcher】开始搜索 %s ..." % first_search_name) media_list = self.search_medias(key_word=first_search_name, filter_args=filter_args, match_media=media_info, @@ -148,7 +148,7 @@ def search_one_media(self, media_info, if len(media_list) == 0 \ and second_search_name \ and second_search_name != first_search_name: - log.info("【Searcher】%s 未检索到资源,尝试通过 %s 重新检索 ..." % (first_search_name, second_search_name)) + log.info("【Searcher】%s 未搜索到资源,尝试通过 %s 重新搜索 ..." % (first_search_name, second_search_name)) media_list = self.search_medias(key_word=second_search_name, filter_args=filter_args, match_media=media_info, diff --git a/app/sites/site_cookie.py b/app/sites/site_cookie.py index 182a6d6a..15d0e055 100644 --- a/app/sites/site_cookie.py +++ b/app/sites/site_cookie.py @@ -301,7 +301,7 @@ def update_sites_cookie_ua(self, @staticmethod def get_captcha_base64(chrome, image_url): """ - 根据图片地址,获取验证码图片base64编码 + 根据图片地址,使用浏览器获取验证码图片base64编码 """ if not image_url: return "" diff --git a/app/sites/site_signin.py b/app/sites/site_signin.py index 96ca0294..91c13775 100644 --- a/app/sites/site_signin.py +++ b/app/sites/site_signin.py @@ -68,8 +68,11 @@ def __signin_site(self, site_info): 签到一个站点 """ site_module = self.__build_class(site_info.get("signurl")) - if site_module: - return site_module.signin(site_info) + if site_module and hasattr(site_module, "signin"): + try: + return site_module().signin(site_info) + except Exception as e: + return f"【{site_info.get('name')}】签到失败:{str(e)}" else: return self.__signin_base(site_info) diff --git a/app/sites/sitesignin/hdsky.py b/app/sites/sitesignin/hdsky.py new file mode 100644 index 00000000..115acab3 --- /dev/null +++ b/app/sites/sitesignin/hdsky.py @@ -0,0 +1,102 @@ +import json +import time + +import log +from app.helper import OcrHelper +from app.sites.sitesignin._base import _ISiteSigninHandler +from app.utils import StringUtils, RequestUtils +from config import Config + + +class HDSky(_ISiteSigninHandler): + """ + 天空ocr签到 + """ + # 匹配的站点Url,每一个实现类都需要设置为自己的站点Url + site_url = "hdsky.me" + + @classmethod + def match(cls, url): + """ + 根据站点Url判断是否匹配当前站点签到类,大部分情况使用默认实现即可 + :param url: 站点Url + :return: 是否匹配,如匹配则会调用该类的signin方法 + """ + return True if StringUtils.url_equal(url, cls.site_url) else False + + def signin(self, site_info: dict): + """ + 执行签到操作 + :param site_info: 站点信息,含有站点Url、站点Cookie、UA等信息 + :return: 签到结果信息 + """ + site_cookie = site_info.get("cookie") + ua = site_info.get("ua") + + # 获取验证码请求,考虑到网络问题获取失败,多获取几次试试 + res_times = 0 + img_hash = None + while not img_hash and res_times <= 3: + image_res = RequestUtils(cookies=site_cookie, + headers=ua, + proxies=Config().get_proxies() if site_info.get("proxy") else None + ).post_res(url='https://hdsky.me/image_code_ajax.php', + data={'action': 'new'}) + if image_res and image_res.status_code == 200: + image_json = json.loads(image_res.text) + if image_json["success"]: + img_hash = image_json["code"] + break + res_times += 1 + log.debug(f"【Sites】获取天空验证码失败,正在进行重试,目前重试次数 {res_times}") + time.sleep(1) + + # 获取到二维码hash + if img_hash: + # 完整验证码url + img_get_url = 'https://hdsky.me/image.php?action=regimage&imagehash=%s' % img_hash + log.debug(f"【Sites】获取到天空验证码连接 {img_get_url}") + # ocr识别多次,获取6位验证码 + times = 0 + ocr_result = None + # 识别几次 + while times <= 3: + # ocr二维码识别 + ocr_result = OcrHelper().get_captcha_text(image_url=img_get_url, + cookie=site_cookie, + ua=ua) + log.debug(f"【Sites】orc识别天空验证码 {ocr_result}") + if ocr_result: + if len(ocr_result) == 6: + log.info(f"【Sites】orc识别天空验证码成功 {ocr_result}") + break + times += 1 + log.debug(f"【Sites】orc识别天空验证码失败,正在进行重试,目前重试次数 {times}") + time.sleep(1) + + if ocr_result: + # 组装请求参数 + data = { + 'action': 'showup', + 'imagehash': img_hash, + 'imagestring': ocr_result + } + # 访问签到链接 + res = RequestUtils(cookies=site_cookie, + headers=ua, + proxies=Config().get_proxies() if site_info.get("proxy") else None + ).post_res(url='https://hdsky.me/showup.php', data=data) + if res and res.status_code == 200: + if json.loads(res.text)["success"]: + log.info(f"【Sites】天空签到成功") + return '【天空】签到成功' + elif str(json.loads(res.text)["message"]) == "date_unmatch": + # 重复签到 + log.warn(f"【Sites】天空重复成功") + return '【天空】今日已签到' + elif str(json.loads(res.text)["message"]) == "invalid_imagehash": + # 验证码错误 + log.warn(f"【Sites】天空签到失败:验证码错误") + return '【Sites】天空签到失败:验证码错误' + + return '【Sites】天空签到失败:未获取到验证码' diff --git a/app/sites/sitesignin/tjupt.py b/app/sites/sitesignin/tjupt.py new file mode 100644 index 00000000..a3a8a040 --- /dev/null +++ b/app/sites/sitesignin/tjupt.py @@ -0,0 +1,182 @@ +import json +import re +from io import BytesIO + +from lxml import etree +from PIL import Image +import log +from app.sites.sitesignin._base import _ISiteSigninHandler +from app.utils import StringUtils, RequestUtils +from config import Config + + +class Tjupt(_ISiteSigninHandler): + """ + 北洋签到 + """ + # 匹配的站点Url,每一个实现类都需要设置为自己的站点Url + site_url = "tjupt.org" + + # 签到地址 + _sign_in_url = 'https://www.tjupt.org/attendance.php' + + # 签到成功 + _succeed_regex = ['这是您的首次签到,本次签到获得.*?个魔力值。', + '签到成功,这是您的第.*?次签到,已连续签到.*?天,本次签到获得.*?个魔力值。', + '重新签到成功,本次签到获得.*?个魔力值'], + + @classmethod + def match(cls, url): + """ + 根据站点Url判断是否匹配当前站点签到类,大部分情况使用默认实现即可 + :param url: 站点Url + :return: 是否匹配,如匹配则会调用该类的signin方法 + """ + return True if StringUtils.url_equal(url, cls.site_url) else False + + def signin(self, site_info: dict): + """ + 执行签到操作 + :param site_info: 站点信息,含有站点Url、站点Cookie、UA等信息 + :return: 签到结果信息 + """ + site_cookie = site_info.get("cookie") + ua = site_info.get("ua") + + # 获取北洋签到页面html + html_res = RequestUtils(cookies=site_cookie, + headers=ua, + proxies=Config().get_proxies() if site_info.get("proxy") else None + ).get_res(url=self._sign_in_url) + + # 获取签到后返回html,判断是否签到成功 + self.__sign_in_result(html_res=html_res.text) + + # 没有签到则解析html + html = etree.HTML(html_res.text) + if not html: + return + img_url = html.xpath('//table[@class="captcha"]//img/@src')[0] + if img_url: + # 签到图片 + img_url = "https://www.tjupt.org" + img_url + log.info(f"【Sites】获取到北洋签到图片 {img_url}") + # 获取签到图片hash + captcha_img_res = RequestUtils(cookies=site_cookie, + headers=ua, + proxies=Config().get_proxies() if site_info.get("proxy") else None + ).get_res(url=img_url) + if not captcha_img_res or captcha_img_res.status_code != 200: + log.error(f"【Sites】北洋签到图片 {img_url} 请求失败") + return '【北洋】签到失败,未获取到签到图片' + captcha_img = Image.open(BytesIO(captcha_img_res.content)) + captcha_img_hash = self._tohash(captcha_img) + log.info(f"【Sites】北洋签到图片hash {captcha_img_hash}") + + # 签到答案选项 + values = html.xpath("//input[@name='answer']/@value") + options = html.xpath("//input[@name='answer']/text()") + # value+选项 + answers = list(zip(values, options)) + for value, answer in answers: + if answer: + # 豆瓣检索 + db_res = RequestUtils().get_res(url=f'https://movie.douban.com/j/subject_suggest?q={answer}') + if not db_res or db_res.status_code != 200: + log.warn(f"【Sites】北洋签到选项 {answer} 未查询到豆瓣数据") + continue + # 豆瓣返回结果 + db_answers = json.loads(db_res.text) + if not isinstance(db_answers, list): + db_answers = [db_answers] + + for db_answer in db_answers: + answer_title = db_answer['title'] + answer_img_url = db_answer['img'] + + # 获取答案hash + answer_img_res = RequestUtils().get_res(url=answer_img_url) + if not answer_img_res or answer_img_res.status_code != 200: + log.error(f"【Sites】北洋签到答案 {answer_title} {answer_img_url} 请求失败") + return '【北洋】签到失败,获取签到答案图片失败' + answer_img = Image.open(BytesIO(answer_img_res.content)) + answer_img_hash = self._tohash(answer_img) + log.info(f"【Sites】北洋签到答案图片hash {answer_title} {answer_img_hash}") + + # 获取选项图片与签到图片相似度,大于0.9默认是正确答案 + score = self._comparehash(captcha_img_hash, answer_img_hash) + log.info(f"【Sites】北洋签到图片与选项 {answer} 豆瓣图片相似度 {score}") + if score > 0.9: + # 确实是答案 + data = { + 'answer': value, + 'submit': '提交' + } + log.info(f"提交data {data}") + sign_in_res = RequestUtils(cookies=site_cookie, + headers=ua, + proxies=Config().get_proxies() if site_info.get( + "proxy") else None + ).post_res(url=self._sign_in_url, data=data) + if not sign_in_res or sign_in_res.status_code != 200: + log.error(f"【Sites】北洋签到失败,签到接口请求失败") + return '【北洋】签到失败,签到接口请求失败' + + # 获取签到后返回html,判断是否签到成功 + self.__sign_in_result(html_res=sign_in_res.text) + + log.error(f"【Sites】北洋签到失败,未获取到匹配答案") + # 没有匹配签到成功,则签到失败 + return '【北洋】签到失败,未获取到匹配答案' + + def __sign_in_result(self, html_res): + """ + 判断是否签到成功 + """ + html_text = self._prepare_html_text(html_res.text) + for regex in self._succeed_regex: + if re.search(str(regex), html_text): + log.info(f"【Sites】北洋签到成功") + return '【北洋】签到成功' + + @staticmethod + def _tohash(img, shape=(10, 10)): + """ + 获取图片hash + """ + img = img.resize(shape) + gray = img.convert('L') + s = 0 + hash_str = '' + for i in range(shape[1]): + for j in range(shape[0]): + s = s + gray.getpixel((j, i)) + avg = s / (shape[0] * shape[1]) + for i in range(shape[1]): + for j in range(shape[0]): + if gray.getpixel((j, i)) > avg: + hash_str = hash_str + '1' + else: + hash_str = hash_str + '0' + return hash_str + + @staticmethod + def _comparehash(hash1, hash2, shape=(10, 10)): + """ + 比较图片hash + 返回相似度 + """ + n = 0 + if len(hash1) != len(hash2): + return -1 + for i in range(len(hash1)): + if hash1[i] == hash2[i]: + n = n + 1 + return n / (shape[0] * shape[1]) + + @staticmethod + def _prepare_html_text(html_text): + """ + 处理掉HTML中的干扰部分 + """ + return re.sub(r"#\d+", "", re.sub(r"\d+px", "", html_text)) diff --git a/app/subscribe.py b/app/subscribe.py index ff3c1c1b..9440e88d 100644 --- a/app/subscribe.py +++ b/app/subscribe.py @@ -154,7 +154,7 @@ def add_rss_subscribe(self, mtype, name, year, rss_sites = default_rss_sites if not search_sites and default_search_sites: search_sites = default_search_sites - # 检索媒体信息 + # 搜索媒体信息 if not fuzzy_match: # 根据TMDBID查询,从推荐加订阅的情况 if mediaid: @@ -666,7 +666,7 @@ def subscribe_search_all(self): def subscribe_search(self, state="D"): """ - RSS订阅队列中状态的任务处理,先进行存量资源检索,缺失的才标志为RSS状态,由定时服务调用 + RSS订阅队列中状态的任务处理,先进行存量资源搜索,缺失的才标志为RSS状态,由定时服务调用 """ try: lock.acquire() @@ -679,16 +679,16 @@ def subscribe_search(self, state="D"): def subscribe_search_movie(self, rssid=None, state='D'): """ - 检索电影RSS - :param rssid: 订阅ID,未输入时检索所有状态为D的,输入时检索该ID任何状态的 - :param state: 检索的状态,默认为队列中才检索 + 搜索电影RSS + :param rssid: 订阅ID,未输入时搜索所有状态为D的,输入时搜索该ID任何状态的 + :param state: 搜索的状态,默认为队列中才搜索 """ if rssid: rss_movies = self.get_subscribe_movies(rid=rssid) else: rss_movies = self.get_subscribe_movies(state=state) if rss_movies: - log.info("【Subscribe】共有 %s 个电影订阅需要检索" % len(rss_movies)) + log.info("【Subscribe】共有 %s 个电影订阅需要搜索" % len(rss_movies)) for rid, rss_info in rss_movies.items(): # 跳过模糊匹配的 if rss_info.get("fuzzy_match"): @@ -703,64 +703,70 @@ def subscribe_search_movie(self, rssid=None, state='D'): # 开始搜索 self.dbhelper.update_rss_movie_state(rssid=rssid, state='S') - # 识别 - media_info = self.__get_media_info(tmdbid, name, year, MediaType.MOVIE) - # 未识别到媒体信息 - if not media_info or not media_info.tmdb_info: - self.dbhelper.update_rss_movie_state(rssid=rssid, state='R') - continue - media_info.set_download_info(download_setting=rss_info.get("download_setting"), - save_path=rss_info.get("save_path")) - # 自定义搜索词 - media_info.keyword = keyword - # 非洗版的情况检查是否存在 - if not over_edition: - # 检查是否存在 - exist_flag, no_exists, _ = self.downloader.check_exists_medias(meta_info=media_info) - # 已经存在 - if exist_flag: - log.info("【Subscribe】电影 %s 已存在" % media_info.get_title_string()) - self.finish_rss_subscribe(rssid=rssid, media=media_info) + + try: + # 识别 + media_info = self.__get_media_info(tmdbid, name, year, MediaType.MOVIE) + # 未识别到媒体信息 + if not media_info or not media_info.tmdb_info: + self.dbhelper.update_rss_movie_state(rssid=rssid, state='R') continue - else: - # 洗版时按缺失来下载 - no_exists = {} - # 把洗版标志加入检索 - media_info.over_edition = over_edition - # 将当前的优先级传入搜索 - media_info.res_order = self.dbhelper.get_rss_overedition_order(rtype=media_info.type, - rssid=rssid) - # 开始检索 - filter_dict = { - "restype": rss_info.get('filter_restype'), - "pix": rss_info.get('filter_pix'), - "team": rss_info.get('filter_team'), - "rule": rss_info.get('filter_rule'), - "include": rss_info.get('filter_include'), - "exclude": rss_info.get('filter_exclude'), - "site": rss_info.get("search_sites") - } - search_result, _, _, _ = self.searcher.search_one_media( - media_info=media_info, - in_from=SearchType.RSS, - no_exists=no_exists, - sites=rss_info.get("search_sites"), - filters=filter_dict) - if search_result: - # 洗版 - if over_edition: - self.update_subscribe_over_edition(rtype=search_result.type, - rssid=rssid, - media=search_result) + media_info.set_download_info(download_setting=rss_info.get("download_setting"), + save_path=rss_info.get("save_path")) + # 自定义搜索词 + media_info.keyword = keyword + # 非洗版的情况检查是否存在 + if not over_edition: + # 检查是否存在 + exist_flag, no_exists, _ = self.downloader.check_exists_medias(meta_info=media_info) + # 已经存在 + if exist_flag: + log.info("【Subscribe】电影 %s 已存在" % media_info.get_title_string()) + self.finish_rss_subscribe(rssid=rssid, media=media_info) + continue else: - self.finish_rss_subscribe(rssid=rssid, media=media_info) - else: + # 洗版时按缺失来下载 + no_exists = {} + # 把洗版标志加入搜索 + media_info.over_edition = over_edition + # 将当前的优先级传入搜索 + media_info.res_order = self.dbhelper.get_rss_overedition_order(rtype=media_info.type, + rssid=rssid) + # 开始搜索 + filter_dict = { + "restype": rss_info.get('filter_restype'), + "pix": rss_info.get('filter_pix'), + "team": rss_info.get('filter_team'), + "rule": rss_info.get('filter_rule'), + "include": rss_info.get('filter_include'), + "exclude": rss_info.get('filter_exclude'), + "site": rss_info.get("search_sites") + } + search_result, _, _, _ = self.searcher.search_one_media( + media_info=media_info, + in_from=SearchType.RSS, + no_exists=no_exists, + sites=rss_info.get("search_sites"), + filters=filter_dict) + if search_result: + # 洗版 + if over_edition: + self.update_subscribe_over_edition(rtype=search_result.type, + rssid=rssid, + media=search_result) + else: + self.finish_rss_subscribe(rssid=rssid, media=media_info) + else: + self.dbhelper.update_rss_movie_state(rssid=rssid, state='R') + except Exception as err: self.dbhelper.update_rss_movie_state(rssid=rssid, state='R') + log.error(f"【Subscribe】电影 {name} 订阅搜索失败:{str(err)}") + continue def subscribe_search_tv(self, rssid=None, state="D"): """ - 检索电视剧RSS - :param rssid: 订阅ID,未输入时检索所有状态为D的,输入时检索该ID任何状态的 + 搜索电视剧RSS + :param rssid: 订阅ID,未输入时搜索所有状态为D的,输入时检索该ID任何状态的 :param state: 检索的状态,默认为队列中才检索 """ if rssid: @@ -780,115 +786,121 @@ def subscribe_search_tv(self, rssid=None, state="D"): tmdbid = rss_info.get("tmdbid") over_edition = rss_info.get("over_edition") keyword = rss_info.get("keyword") + # 开始搜索 self.dbhelper.update_rss_tv_state(rssid=rssid, state='S') - # 识别 - media_info = self.__get_media_info(tmdbid, name, year, MediaType.TV) - # 未识别到媒体信息 - if not media_info or not media_info.tmdb_info: - self.dbhelper.update_rss_tv_state(rssid=rssid, state='R') - continue - # 取下载设置 - media_info.set_download_info(download_setting=rss_info.get("download_setting"), - save_path=rss_info.get("save_path")) - # 从登记薄中获取缺失剧集 - season = 1 - if rss_info.get("season"): - season = int(str(rss_info.get("season")).replace("S", "")) - # 订阅季 - media_info.begin_season = season - # 订阅ID - media_info.rssid = rssid - # 自定义集数 - total_ep = rss_info.get("total") - current_ep = rss_info.get("current_ep") - # 自定义搜索词 - media_info.keyword = keyword - # 表中记录的剩余订阅集数 - episodes = self.get_subscribe_tv_episodes(rss_info.get("id")) - if episodes is None: - episodes = [] - if current_ep: - episodes = list(range(current_ep, total_ep + 1)) - rss_no_exists[media_info.tmdb_id] = [ - { - "season": season, - "episodes": episodes, - "total_episodes": total_ep - } - ] - else: - rss_no_exists[media_info.tmdb_id] = [ - { - "season": season, - "episodes": episodes, - "total_episodes": total_ep - } - ] - # 非洗版时检查本地媒体库情况 - if not over_edition: - exist_flag, library_no_exists, _ = self.downloader.check_exists_medias( - meta_info=media_info, - total_ep={season: total_ep}) - # 当前剧集已存在,跳过 - if exist_flag: - # 已全部存在 - if not library_no_exists \ - or not library_no_exists.get(media_info.tmdb_id): - log.info("【Subscribe】电视剧 %s 订阅剧集已全部存在" % ( - media_info.get_title_string())) - # 完成订阅 - self.finish_rss_subscribe(rssid=rss_info.get("id"), - media=media_info) + + try: + # 识别 + media_info = self.__get_media_info(tmdbid, name, year, MediaType.TV) + # 未识别到媒体信息 + if not media_info or not media_info.tmdb_info: + self.dbhelper.update_rss_tv_state(rssid=rssid, state='R') continue - # 取交集做为缺失集 - rss_no_exists = Torrent.get_intersection_episodes(target=rss_no_exists, - source=library_no_exists, - title=media_info.tmdb_id) - if rss_no_exists.get(media_info.tmdb_id): - log.info("【Subscribe】%s 订阅缺失季集:%s" % ( - media_info.get_title_string(), - rss_no_exists.get(media_info.tmdb_id) - )) - else: - # 把洗版标志加入检索 - media_info.over_edition = over_edition - # 将当前的优先级传入检索 - media_info.res_order = self.dbhelper.get_rss_overedition_order(rtype=MediaType.TV, - rssid=rssid) - - # 开始检索 - filter_dict = { - "restype": rss_info.get('filter_restype'), - "pix": rss_info.get('filter_pix'), - "team": rss_info.get('filter_team'), - "rule": rss_info.get('filter_rule'), - "include": rss_info.get('filter_include'), - "exclude": rss_info.get('filter_exclude'), - "site": rss_info.get("search_sites") - } - search_result, no_exists, _, _ = self.searcher.search_one_media( - media_info=media_info, - in_from=SearchType.RSS, - no_exists=rss_no_exists, - sites=rss_info.get("search_sites"), - filters=filter_dict) - if search_result \ - or not no_exists \ - or not no_exists.get(media_info.tmdb_id): - # 洗版 - if over_edition: - self.update_subscribe_over_edition(rtype=media_info.type, - rssid=rssid, - media=search_result) + # 取下载设置 + media_info.set_download_info(download_setting=rss_info.get("download_setting"), + save_path=rss_info.get("save_path")) + # 从登记薄中获取缺失剧集 + season = 1 + if rss_info.get("season"): + season = int(str(rss_info.get("season")).replace("S", "")) + # 订阅季 + media_info.begin_season = season + # 订阅ID + media_info.rssid = rssid + # 自定义集数 + total_ep = rss_info.get("total") + current_ep = rss_info.get("current_ep") + # 自定义搜索词 + media_info.keyword = keyword + # 表中记录的剩余订阅集数 + episodes = self.get_subscribe_tv_episodes(rss_info.get("id")) + if episodes is None: + episodes = [] + if current_ep: + episodes = list(range(current_ep, total_ep + 1)) + rss_no_exists[media_info.tmdb_id] = [ + { + "season": season, + "episodes": episodes, + "total_episodes": total_ep + } + ] + else: + rss_no_exists[media_info.tmdb_id] = [ + { + "season": season, + "episodes": episodes, + "total_episodes": total_ep + } + ] + # 非洗版时检查本地媒体库情况 + if not over_edition: + exist_flag, library_no_exists, _ = self.downloader.check_exists_medias( + meta_info=media_info, + total_ep={season: total_ep}) + # 当前剧集已存在,跳过 + if exist_flag: + # 已全部存在 + if not library_no_exists \ + or not library_no_exists.get(media_info.tmdb_id): + log.info("【Subscribe】电视剧 %s 订阅剧集已全部存在" % ( + media_info.get_title_string())) + # 完成订阅 + self.finish_rss_subscribe(rssid=rss_info.get("id"), + media=media_info) + continue + # 取交集做为缺失集 + rss_no_exists = Torrent.get_intersection_episodes(target=rss_no_exists, + source=library_no_exists, + title=media_info.tmdb_id) + if rss_no_exists.get(media_info.tmdb_id): + log.info("【Subscribe】%s 订阅缺失季集:%s" % ( + media_info.get_title_string(), + rss_no_exists.get(media_info.tmdb_id) + )) else: - # 完成订阅 - self.finish_rss_subscribe(rssid=rssid, media=media_info) - elif no_exists: - # 更新状态 - self.update_subscribe_tv_lack(rssid=rssid, - media_info=media_info, - seasoninfo=no_exists.get(media_info.tmdb_id)) + # 把洗版标志加入检索 + media_info.over_edition = over_edition + # 将当前的优先级传入检索 + media_info.res_order = self.dbhelper.get_rss_overedition_order(rtype=MediaType.TV, + rssid=rssid) + # 开始检索 + filter_dict = { + "restype": rss_info.get('filter_restype'), + "pix": rss_info.get('filter_pix'), + "team": rss_info.get('filter_team'), + "rule": rss_info.get('filter_rule'), + "include": rss_info.get('filter_include'), + "exclude": rss_info.get('filter_exclude'), + "site": rss_info.get("search_sites") + } + search_result, no_exists, _, _ = self.searcher.search_one_media( + media_info=media_info, + in_from=SearchType.RSS, + no_exists=rss_no_exists, + sites=rss_info.get("search_sites"), + filters=filter_dict) + if search_result \ + or not no_exists \ + or not no_exists.get(media_info.tmdb_id): + # 洗版 + if over_edition: + self.update_subscribe_over_edition(rtype=media_info.type, + rssid=rssid, + media=search_result) + else: + # 完成订阅 + self.finish_rss_subscribe(rssid=rssid, media=media_info) + elif no_exists: + # 更新状态 + self.update_subscribe_tv_lack(rssid=rssid, + media_info=media_info, + seasoninfo=no_exists.get(media_info.tmdb_id)) + except Exception as err: + log.error(f"【Subscribe】电视剧 {name} 订阅搜索失败:{str(err)}") + self.dbhelper.update_rss_tv_state(rssid=rssid, state='R') + continue def update_rss_state(self, rtype, rssid, state): """ diff --git a/app/utils/string_utils.py b/app/utils/string_utils.py index cdcd39ce..dc5f497a 100644 --- a/app/utils/string_utils.py +++ b/app/utils/string_utils.py @@ -276,7 +276,7 @@ def clear_file_name(name): @staticmethod def get_keyword_from_string(content): """ - 从检索关键字中拆分中年份、季、集、类型 + 从搜索关键字中拆分中年份、季、集、类型 """ if not content: return None, None, None, None, None diff --git a/config/config.yaml b/config/config.yaml index 444005d8..54e547de 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -180,9 +180,9 @@ scraper_pic: # 开启后,读取视频文件生成缩略图 episode_thumb_ffmpeg: false -# 【配置站点检索信息】 +# 【配置站点搜索信息】 pt: - #【聚合检索使用的检索器】:builtin + #【聚合搜索使用的检索器】:builtin search_indexer: builtin # 【内建索引器使用的站点】:只有在该站点列表中内建索引器搜索时才会使用 indexer_sites: @@ -243,6 +243,6 @@ laboratory: # 【默认搜索豆瓣资源】:开启将使用豆瓣进行电影电视剧的名称搜索,否则使用TMDB的数据 use_douban_titles: false # 【精确搜索使用英文名称】:开启后对于精确搜索场景(远程搜索、订阅搜索等)将会使用英文名检索站点资源以提升匹配度,但对有些站点资源标题全是中文的则需要关闭,否则匹配不到 - search_en_title: true + search_en_title: false # 【使用TMDB代理】 tmdb_proxy: false diff --git a/config/sites.dat b/config/sites.dat index 83fbcc51..9053463e 100644 Binary files a/config/sites.dat and b/config/sites.dat differ diff --git a/requirements.txt b/requirements.txt index e6064127..d08a2db8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -55,6 +55,7 @@ parsel==1.6.0 parso==0.8.3 pexpect==4.8.0 pickleshare==0.7.5 +pillow==9.5.0 proces==0.1.2 prompt-toolkit==3.0.31 psutil==5.9.4 diff --git a/version.py b/version.py index 1f4a4e72..1b34ca3b 100644 --- a/version.py +++ b/version.py @@ -1 +1 @@ -APP_VERSION = 'v3.1.5' +APP_VERSION = 'v3.2.0' diff --git a/web/action.py b/web/action.py index 78e95a24..3ee95f0a 100644 --- a/web/action.py +++ b/web/action.py @@ -375,7 +375,7 @@ def handle_message_job(msg, in_from=SearchType.OT, user_id=None, user_name=None) message.send_channel_msg( channel=in_from, title="正在运行 %s ..." % command.get("desp"), user_id=user_id) else: - # 站点检索或者添加订阅 + # 站点搜索或者添加订阅 ThreadHelper().start_thread(search_media_by_message, (msg, in_from, user_id, user_name)) @@ -516,7 +516,7 @@ def __sch(data): @staticmethod def __search(data): """ - WEB检索资源 + WEB搜索资源 """ search_word = data.get("search_word") ident_flag = False if data.get("unident") else True diff --git a/web/backend/search_torrents.py b/web/backend/search_torrents.py index d373a358..7ac57ff4 100644 --- a/web/backend/search_torrents.py +++ b/web/backend/search_torrents.py @@ -32,7 +32,7 @@ def search_medias_for_web(content, ident_flag=True, filters=None, tmdbid=None, m """ mtype, key_word, season_num, episode_num, year, content = StringUtils.get_keyword_from_string(content) if not key_word: - log.info("【Web】%s 检索关键字有误!" % content) + log.info("【Web】%s 搜索关键字有误!" % content) return -1, "%s 未识别到搜索关键字!" % content # 类型 if media_type: @@ -128,8 +128,8 @@ def search_medias_for_web(content, ident_flag=True, filters=None, tmdbid=None, m # 整合高级查询条件 if filters: filter_args.update(filters) - # 开始检索 - log.info("【Web】开始检索 %s ..." % content) + # 开始搜索 + log.info("【Web】开始搜索 %s ..." % content) media_list = Searcher().search_medias(key_word=first_search_name, filter_args=filter_args, match_media=media_info, @@ -141,8 +141,8 @@ def search_medias_for_web(content, ident_flag=True, filters=None, tmdbid=None, m and second_search_name != first_search_name: search_process.start(ProgressKey.Search) search_process.update(ptype=ProgressKey.Search, - text="%s 未检索到资源,尝试通过 %s 重新检索 ..." % (first_search_name, second_search_name)) - log.info("【Searcher】%s 未检索到资源,尝试通过 %s 重新检索 ..." % (first_search_name, second_search_name)) + text="%s 未搜索到资源,尝试通过 %s 重新搜索 ..." % (first_search_name, second_search_name)) + log.info("【Searcher】%s 未搜索到资源,尝试通过 %s 重新搜索 ..." % (first_search_name, second_search_name)) media_list = Searcher().search_medias(key_word=second_search_name, filter_args=filter_args, match_media=media_info, @@ -153,10 +153,10 @@ def search_medias_for_web(content, ident_flag=True, filters=None, tmdbid=None, m # 结束进度 search_process.end(ProgressKey.Search) if len(media_list) == 0: - log.info("【Web】%s 未检索到任何资源" % content) - return 1, "%s 未检索到任何资源" % content + log.info("【Web】%s 未搜索到任何资源" % content) + return 1, "%s 未搜索到任何资源" % content else: - log.info("【Web】共检索到 %s 个有效资源" % len(media_list)) + log.info("【Web】共搜索到 %s 个有效资源" % len(media_list)) # 插入数据库 media_list = sorted(media_list, key=lambda x: "%s%s%s" % (str(x.res_order).rjust(3, '0'), str(x.site_order).rjust(3, '0'), @@ -169,7 +169,7 @@ def search_medias_for_web(content, ident_flag=True, filters=None, tmdbid=None, m def search_media_by_message(input_str, in_from: SearchType, user_id, user_name=None): """ - 输入字符串,解析要求并进行资源检索 + 输入字符串,解析要求并进行资源搜索 :param input_str: 输入字符串,可以包括标题、年份、季、集的信息,使用空格隔开 :param in_from: 搜索下载的请求来源 :param user_id: 需要发送消息的,传入该参数,则只给对应用户发送交互消息 @@ -180,7 +180,7 @@ def search_media_by_message(input_str, in_from: SearchType, user_id, user_name=N global SEARCH_MEDIA_CACHE if not input_str: - log.info("【Searcher】检索关键字有误!") + log.info("【Searcher】搜索关键字有误!") return else: input_str = str(input_str).strip() @@ -412,9 +412,9 @@ def __search_media(in_from, media_info, user_id, user_name=None): if exist_flag: return - # 开始检索 + # 开始搜索 Message().send_channel_msg(channel=in_from, - title="开始检索 %s ..." % media_info.title, + title="开始搜索 %s ..." % media_info.title, user_id=user_id) search_result, no_exists, search_count, download_count = Searcher().search_one_media(media_info=media_info, in_from=in_from, diff --git a/web/static/css/style.css b/web/static/css/style.css index c67a6531..7a44527f 100644 --- a/web/static/css/style.css +++ b/web/static/css/style.css @@ -145,6 +145,10 @@ body, .page { grid-template-columns: repeat(auto-fill,minmax(20rem,1fr)); } +.grid-large-card { + grid-template-columns: repeat(auto-fill,minmax(24rem,1fr)); +} + .offcanvas-backdrop.show { opacity: 0.5 !important; background-color: #000 !important; diff --git a/web/templates/download/downloading.html b/web/templates/download/downloading.html index 85b1f745..c5f609df 100644 --- a/web/templates/download/downloading.html +++ b/web/templates/download/downloading.html @@ -36,14 +36,14 @@