From b3c0dc813b2d7dcc650e14dbdf5938e7d8b01327 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Mon, 25 Sep 2023 07:12:36 +0800 Subject: [PATCH 1/4] fix #662 --- app/modules/filetransfer/__init__.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/modules/filetransfer/__init__.py b/app/modules/filetransfer/__init__.py index 390e5f42..64d34ad9 100644 --- a/app/modules/filetransfer/__init__.py +++ b/app/modules/filetransfer/__init__.py @@ -536,16 +536,14 @@ class FileTransferModule(_ModuleBase): @staticmethod def get_library_path(path: Path): """ - 根据目录查询其所在的媒体库目录 + 根据目录查询其所在的媒体库目录,查询不到的返回输入目录 """ if not path: return None if not settings.LIBRARY_PATHS: - return None + return path # 目的路径,多路径以,分隔 dest_paths = settings.LIBRARY_PATHS - if len(dest_paths) == 1: - return dest_paths[0] for libpath in dest_paths: try: if path.is_relative_to(libpath): @@ -553,7 +551,7 @@ class FileTransferModule(_ModuleBase): except Exception as e: logger.debug(f"计算媒体库路径时出错:{e}") continue - return None + return path @staticmethod def get_target_path(in_path: Path = None) -> Optional[Path]: From da4ff9957059ead7fa4e8d362656fd39c1d6dc11 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Mon, 25 Sep 2023 08:40:19 +0800 Subject: [PATCH 2/4] fix #655 --- app/plugins/bestfilmversion/__init__.py | 406 ++++++++++++------------ app/plugins/dirmonitor/__init__.py | 2 +- app/plugins/mediasyncdel/__init__.py | 22 +- app/plugins/speedlimiter/__init__.py | 140 ++++---- 4 files changed, 294 insertions(+), 276 deletions(-) diff --git a/app/plugins/bestfilmversion/__init__.py b/app/plugins/bestfilmversion/__init__.py index 1e516e6a..69063912 100644 --- a/app/plugins/bestfilmversion/__init__.py +++ b/app/plugins/bestfilmversion/__init__.py @@ -131,130 +131,130 @@ class BestFilmVersion(_PluginBase): 拼装插件配置页面,需要返回两块数据:1、页面配置;2、数据结构 """ return [ - { - 'component': 'VForm', - 'content': [ - { - 'component': 'VRow', - 'content': [ - { - 'component': 'VCol', - 'props': { - 'cols': 12, - 'md': 3 - }, - 'content': [ - { - 'component': 'VSwitch', - 'props': { - 'model': 'enabled', - 'label': '启用插件', - } - } - ] - }, - { - 'component': 'VCol', - 'props': { - 'cols': 12, - 'md': 3 - }, - 'content': [ - { - 'component': 'VSwitch', - 'props': { - 'model': 'notify', - 'label': '发送通知', - } - } - ] - }, - { - 'component': 'VCol', - 'props': { - 'cols': 12, - 'md': 3 - }, - 'content': [ - { - 'component': 'VSwitch', - 'props': { - 'model': 'only_once', - 'label': '立即运行一次', - } - } - ] - }, - { - 'component': 'VCol', - 'props': { - 'cols': 12, - 'md': 3 - }, - 'content': [ - { - 'component': 'VSwitch', - 'props': { - 'model': 'webhook_enabled', - 'label': 'Webhook', - } - } - ] - } - ] - }, - { - 'component': 'VRow', - 'content': [ - { - 'component': 'VCol', - 'props': { - 'cols': 12, - }, - 'content': [ - { - 'component': 'VTextField', - 'props': { - 'model': 'cron', - 'label': '执行周期', - 'placeholder': '5位cron表达式,留空自动' - } - } - ] - } - ] - }, - { - 'component': 'VRow', - 'content': [ - { - 'component': 'VCol', - 'props': { - 'cols': 12, - }, - 'content': [ - { - 'component': 'VAlert', - 'props': { - 'text': '支持主动定时获取媒体库数据和Webhook实时触发两种方式,两者只能选其一,' - 'Webhook需要在媒体服务器设置发送Webhook报文。' - 'Plex使用主动获取时,建议执行周期设置大于1小时,' - '收藏Api调用Plex官网接口,有频率限制。' - } - } - ] - } - ] - } - ] - } - ], { - "enabled": False, - "notify": False, - "cron": "*/30 * * * *", - "webhook_enabled": False, - "only_once": False - } + { + 'component': 'VForm', + 'content': [ + { + 'component': 'VRow', + 'content': [ + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 3 + }, + 'content': [ + { + 'component': 'VSwitch', + 'props': { + 'model': 'enabled', + 'label': '启用插件', + } + } + ] + }, + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 3 + }, + 'content': [ + { + 'component': 'VSwitch', + 'props': { + 'model': 'notify', + 'label': '发送通知', + } + } + ] + }, + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 3 + }, + 'content': [ + { + 'component': 'VSwitch', + 'props': { + 'model': 'only_once', + 'label': '立即运行一次', + } + } + ] + }, + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 3 + }, + 'content': [ + { + 'component': 'VSwitch', + 'props': { + 'model': 'webhook_enabled', + 'label': 'Webhook', + } + } + ] + } + ] + }, + { + 'component': 'VRow', + 'content': [ + { + 'component': 'VCol', + 'props': { + 'cols': 12, + }, + 'content': [ + { + 'component': 'VTextField', + 'props': { + 'model': 'cron', + 'label': '执行周期', + 'placeholder': '5位cron表达式,留空自动' + } + } + ] + } + ] + }, + { + 'component': 'VRow', + 'content': [ + { + 'component': 'VCol', + 'props': { + 'cols': 12, + }, + 'content': [ + { + 'component': 'VAlert', + 'props': { + 'text': '支持主动定时获取媒体库数据和Webhook实时触发两种方式,两者只能选其一,' + 'Webhook需要在媒体服务器设置发送Webhook报文。' + 'Plex使用主动获取时,建议执行周期设置大于1小时,' + '收藏Api调用Plex官网接口,有频率限制。' + } + } + ] + } + ] + } + ] + } + ], { + "enabled": False, + "notify": False, + "cron": "*/30 * * * *", + "webhook_enabled": False, + "only_once": False + } def get_page(self) -> List[dict]: """ @@ -386,81 +386,85 @@ class BestFilmVersion(_PluginBase): # 读取历史记录 history = self.get_data('history') or [] - all_item = [] + # 媒体服务器类型,多个以,分隔 + if not settings.MEDIASERVER: + return + media_servers = settings.MEDIASERVER.split(',') + # 读取收藏 - if settings.MEDIASERVER == 'jellyfin': - self.jellyfin_get_items(all_item) - elif settings.MEDIASERVER == 'emby': - self.emby_get_items(all_item) - else: - resp = self.plex_get_watchlist() - if not resp: - return - all_item.extend(resp) + all_items = {} + for media_server in media_servers: + if media_server == 'jellyfin': + all_items['jellyfin'] = self.jellyfin_get_items() + elif media_server == 'emby': + all_items['emby'] = self.emby_get_items() + else: + all_items['plex'] = self.plex_get_watchlist() def function(y, x): return y if (x['Name'] in [i['Name'] for i in y]) else (lambda z, u: (z.append(u), z))(y, x)[1] - # all_item 根据电影名去重 - result = reduce(function, all_item, []) - - for data in result: - # 检查缓存 - if data.get('Name') in caches: - continue - - # 获取详情 - if settings.MEDIASERVER == 'jellyfin': - item_info_resp = Jellyfin().get_iteminfo(itemid=data.get('Id')) - elif settings.MEDIASERVER == 'emby': - item_info_resp = Emby().get_iteminfo(itemid=data.get('Id')) - else: - item_info_resp = self.plex_get_iteminfo(itemid=data.get('Id')) - - logger.info(f'BestFilmVersion插件 item打印 {item_info_resp}') - if not item_info_resp: - continue - - # 只接受Movie类型 - if data.get('Type') != 'Movie': - continue - - # 获取tmdb_id - media_info_ids = item_info_resp.get('ExternalUrls') - if not media_info_ids: - continue - for media_info_id in media_info_ids: - if 'TheMovieDb' != media_info_id.get('Name'): + # 处理所有结果 + for server, all_item in all_items.items(): + # all_item 根据电影名去重 + result = reduce(function, all_item, []) + for data in result: + # 检查缓存 + if data.get('Name') in caches: continue - tmdb_find_id = str(media_info_id.get('Url')).split('/') - tmdb_find_id.reverse() - tmdb_id = tmdb_find_id[0] - # 识别媒体信息 - mediainfo: MediaInfo = self.chain.recognize_media(tmdbid=tmdb_id, mtype=MediaType.MOVIE) - if not mediainfo: - logger.warn(f'未识别到媒体信息,标题:{data.get("Name")},tmdbID:{tmdb_id}') + + # 获取详情 + if server == 'jellyfin': + item_info_resp = Jellyfin().get_iteminfo(itemid=data.get('Id')) + elif server == 'emby': + item_info_resp = Emby().get_iteminfo(itemid=data.get('Id')) + else: + item_info_resp = self.plex_get_iteminfo(itemid=data.get('Id')) + + logger.info(f'BestFilmVersion插件 item打印 {item_info_resp}') + if not item_info_resp: continue - # 添加订阅 - self.subscribechain.add(mtype=MediaType.MOVIE, - title=mediainfo.title, - year=mediainfo.year, - tmdbid=mediainfo.tmdb_id, - best_version=True, - username="收藏洗版", - exist_ok=True) - # 加入缓存 - caches.append(data.get('Name')) - # 存储历史记录 - if mediainfo.tmdb_id not in [h.get("tmdbid") for h in history]: - history.append({ - "title": mediainfo.title, - "type": mediainfo.type.value, - "year": mediainfo.year, - "poster": mediainfo.get_poster_image(), - "overview": mediainfo.overview, - "tmdbid": mediainfo.tmdb_id, - "time": datetime.now().strftime("%Y-%m-%d %H:%M:%S") - }) + + # 只接受Movie类型 + if data.get('Type') != 'Movie': + continue + + # 获取tmdb_id + media_info_ids = item_info_resp.get('ExternalUrls') + if not media_info_ids: + continue + for media_info_id in media_info_ids: + if 'TheMovieDb' != media_info_id.get('Name'): + continue + tmdb_find_id = str(media_info_id.get('Url')).split('/') + tmdb_find_id.reverse() + tmdb_id = tmdb_find_id[0] + # 识别媒体信息 + mediainfo: MediaInfo = self.chain.recognize_media(tmdbid=tmdb_id, mtype=MediaType.MOVIE) + if not mediainfo: + logger.warn(f'未识别到媒体信息,标题:{data.get("Name")},tmdbID:{tmdb_id}') + continue + # 添加订阅 + self.subscribechain.add(mtype=MediaType.MOVIE, + title=mediainfo.title, + year=mediainfo.year, + tmdbid=mediainfo.tmdb_id, + best_version=True, + username="收藏洗版", + exist_ok=True) + # 加入缓存 + caches.append(data.get('Name')) + # 存储历史记录 + if mediainfo.tmdb_id not in [h.get("tmdbid") for h in history]: + history.append({ + "title": mediainfo.title, + "type": mediainfo.type.value, + "year": mediainfo.year, + "poster": mediainfo.get_poster_image(), + "overview": mediainfo.overview, + "tmdbid": mediainfo.tmdb_id, + "time": datetime.now().strftime("%Y-%m-%d %H:%M:%S") + }) # 保存历史记录 self.save_data('history', history) # 保存缓存 @@ -468,13 +472,14 @@ class BestFilmVersion(_PluginBase): finally: lock.release() - def jellyfin_get_items(self, all_item): + def jellyfin_get_items(self) -> List[dict]: # 获取所有user users_url = "{HOST}Users?&apikey={APIKEY}" users = self.get_users(Jellyfin().get_data(users_url)) if not users: logger.info(f"bestfilmversion/users_url: {users_url}") - return + return [] + all_items = [] for user in users: # 根据加入日期 降序排序 url = "{HOST}Users/" + user + "/Items?SortBy=DateCreated%2CSortName" \ @@ -490,14 +495,16 @@ class BestFilmVersion(_PluginBase): resp = self.get_items(Jellyfin().get_data(url)) if not resp: continue - all_item.extend(resp) + all_items.extend(resp) + return all_items - def emby_get_items(self, all_item): + def emby_get_items(self) -> List[dict]: # 获取所有user get_users_url = "{HOST}Users?&api_key={APIKEY}" users = self.get_users(Emby().get_data(get_users_url)) if not users: - return + return [] + all_items = [] for user in users: # 根据加入日期 降序排序 url = "{HOST}emby/Users/" + user + "/Items?SortBy=DateCreated%2CSortName" \ @@ -512,7 +519,8 @@ class BestFilmVersion(_PluginBase): resp = self.get_items(Emby().get_data(url)) if not resp: continue - all_item.extend(resp) + all_items.extend(resp) + return all_items @staticmethod def get_items(resp: Response): @@ -538,7 +546,7 @@ class BestFilmVersion(_PluginBase): return [] @staticmethod - def plex_get_watchlist(): + def plex_get_watchlist() -> List[dict]: # 根据加入日期 降序排序 url = f"https://metadata.provider.plex.tv/library/sections/watchlist/all?type=1&sort=addedAt%3Adesc" \ f"&X-Plex-Container-Start=0&X-Plex-Container-Size=50" \ diff --git a/app/plugins/dirmonitor/__init__.py b/app/plugins/dirmonitor/__init__.py index b0e4396d..ce6e4a89 100644 --- a/app/plugins/dirmonitor/__init__.py +++ b/app/plugins/dirmonitor/__init__.py @@ -598,7 +598,7 @@ class DirMonitor(_PluginBase): 'rows': 5, 'placeholder': '每一行一个目录,支持两种配置方式:\n' '监控目录\n' - '监控目录:转移目的目录' + '监控目录:转移目的目录(需同时在媒体库目录中配置该目的目录)' } } ] diff --git a/app/plugins/mediasyncdel/__init__.py b/app/plugins/mediasyncdel/__init__.py index 5d355fbb..43c12a8d 100644 --- a/app/plugins/mediasyncdel/__init__.py +++ b/app/plugins/mediasyncdel/__init__.py @@ -718,19 +718,21 @@ class MediaSyncDel(_PluginBase): """ # 读取历史记录 history = self.get_data('history') or [] - - # 媒体服务器类型 - media_server = settings.MEDIASERVER - last_time = self.get_data("last_time") del_medias = [] - if media_server == 'emby': - del_medias = self.parse_emby_log(last_time) - elif media_server == 'jellyfin': - del_medias = self.parse_jellyfin_log(last_time) - elif media_server == 'plex': - # TODO plex解析日志 + + # 媒体服务器类型,多个以,分隔 + if not settings.MEDIASERVER: return + media_servers = settings.MEDIASERVER.split(',') + for media_server in media_servers: + if media_server == 'emby': + del_medias.extend(self.parse_emby_log(last_time)) + elif media_server == 'jellyfin': + del_medias.extend(self.parse_jellyfin_log(last_time)) + elif media_server == 'plex': + # TODO plex解析日志 + return if not del_medias: logger.error("未解析到已删除媒体信息") diff --git a/app/plugins/speedlimiter/__init__.py b/app/plugins/speedlimiter/__init__.py index 47cb72a1..501d869a 100644 --- a/app/plugins/speedlimiter/__init__.py +++ b/app/plugins/speedlimiter/__init__.py @@ -383,78 +383,86 @@ class SpeedLimiter(_PluginBase): return # 当前播放的总比特率 total_bit_rate = 0 - # 查询播放中会话 - playing_sessions = [] - if settings.MEDIASERVER == "emby": - req_url = "{HOST}emby/Sessions?api_key={APIKEY}" - try: - res = Emby().get_data(req_url) - if res and res.status_code == 200: - sessions = res.json() - for session in sessions: - if session.get("NowPlayingItem") and not session.get("PlayState", {}).get("IsPaused"): - playing_sessions.append(session) - except Exception as e: - logger.error(f"获取Emby播放会话失败:{str(e)}") - # 计算有效比特率 - for session in playing_sessions: - # 设置了不限速范围则判断session ip是否在不限速范围内 - if self._unlimited_ips["ipv4"] or self._unlimited_ips["ipv6"]: - if not self.__allow_access(self._unlimited_ips, session.get("RemoteEndPoint")) \ - and session.get("NowPlayingItem", {}).get("MediaType") == "Video": - total_bit_rate += int(session.get("NowPlayingItem", {}).get("Bitrate") or 0) - # 未设置不限速范围,则默认不限速内网ip - elif not IpUtils.is_private_ip(session.get("RemoteEndPoint")) \ - and session.get("NowPlayingItem", {}).get("MediaType") == "Video": - total_bit_rate += int(session.get("NowPlayingItem", {}).get("Bitrate") or 0) - elif settings.MEDIASERVER == "jellyfin": - req_url = "{HOST}Sessions?api_key={APIKEY}" - try: - res = Jellyfin().get_data(req_url) - if res and res.status_code == 200: - sessions = res.json() - for session in sessions: - if session.get("NowPlayingItem") and not session.get("PlayState", {}).get("IsPaused"): - playing_sessions.append(session) - except Exception as e: - logger.error(f"获取Jellyfin播放会话失败:{str(e)}") - # 计算有效比特率 - for session in playing_sessions: - # 设置了不限速范围则判断session ip是否在不限速范围内 - if self._unlimited_ips["ipv4"] or self._unlimited_ips["ipv6"]: - if not self.__allow_access(self._unlimited_ips, session.get("RemoteEndPoint")) \ - and session.get("NowPlayingItem", {}).get("MediaType") == "Video": - media_streams = session.get("NowPlayingItem", {}).get("MediaStreams") or [] - for media_stream in media_streams: - total_bit_rate += int(media_stream.get("BitRate") or 0) - # 未设置不限速范围,则默认不限速内网ip - elif not IpUtils.is_private_ip(session.get("RemoteEndPoint")) \ - and session.get("NowPlayingItem", {}).get("MediaType") == "Video": - media_streams = session.get("NowPlayingItem", {}).get("MediaStreams") or [] - for media_stream in media_streams: - total_bit_rate += int(media_stream.get("BitRate") or 0) - elif settings.MEDIASERVER == "plex": - _plex = Plex().get_plex() - if _plex: - sessions = _plex.sessions() - for session in sessions: - bitrate = sum([m.bitrate or 0 for m in session.media]) - playing_sessions.append({ - "type": session.TAG, - "bitrate": bitrate, - "address": session.player.address - }) + # 媒体服务器类型,多个以,分隔 + if not settings.MEDIASERVER: + return + media_servers = settings.MEDIASERVER.split(',') + # 查询所有媒体服务器状态 + for media_server in media_servers: + # 查询播放中会话 + playing_sessions = [] + if media_server == "emby": + req_url = "{HOST}emby/Sessions?api_key={APIKEY}" + try: + res = Emby().get_data(req_url) + if res and res.status_code == 200: + sessions = res.json() + for session in sessions: + if session.get("NowPlayingItem") and not session.get("PlayState", {}).get("IsPaused"): + playing_sessions.append(session) + except Exception as e: + logger.error(f"获取Emby播放会话失败:{str(e)}") + continue # 计算有效比特率 for session in playing_sessions: # 设置了不限速范围则判断session ip是否在不限速范围内 if self._unlimited_ips["ipv4"] or self._unlimited_ips["ipv6"]: - if not self.__allow_access(self._unlimited_ips, session.get("address")) \ + if not self.__allow_access(self._unlimited_ips, session.get("RemoteEndPoint")) \ + and session.get("NowPlayingItem", {}).get("MediaType") == "Video": + total_bit_rate += int(session.get("NowPlayingItem", {}).get("Bitrate") or 0) + # 未设置不限速范围,则默认不限速内网ip + elif not IpUtils.is_private_ip(session.get("RemoteEndPoint")) \ + and session.get("NowPlayingItem", {}).get("MediaType") == "Video": + total_bit_rate += int(session.get("NowPlayingItem", {}).get("Bitrate") or 0) + elif media_server == "jellyfin": + req_url = "{HOST}Sessions?api_key={APIKEY}" + try: + res = Jellyfin().get_data(req_url) + if res and res.status_code == 200: + sessions = res.json() + for session in sessions: + if session.get("NowPlayingItem") and not session.get("PlayState", {}).get("IsPaused"): + playing_sessions.append(session) + except Exception as e: + logger.error(f"获取Jellyfin播放会话失败:{str(e)}") + continue + # 计算有效比特率 + for session in playing_sessions: + # 设置了不限速范围则判断session ip是否在不限速范围内 + if self._unlimited_ips["ipv4"] or self._unlimited_ips["ipv6"]: + if not self.__allow_access(self._unlimited_ips, session.get("RemoteEndPoint")) \ + and session.get("NowPlayingItem", {}).get("MediaType") == "Video": + media_streams = session.get("NowPlayingItem", {}).get("MediaStreams") or [] + for media_stream in media_streams: + total_bit_rate += int(media_stream.get("BitRate") or 0) + # 未设置不限速范围,则默认不限速内网ip + elif not IpUtils.is_private_ip(session.get("RemoteEndPoint")) \ + and session.get("NowPlayingItem", {}).get("MediaType") == "Video": + media_streams = session.get("NowPlayingItem", {}).get("MediaStreams") or [] + for media_stream in media_streams: + total_bit_rate += int(media_stream.get("BitRate") or 0) + elif media_server == "plex": + _plex = Plex().get_plex() + if _plex: + sessions = _plex.sessions() + for session in sessions: + bitrate = sum([m.bitrate or 0 for m in session.media]) + playing_sessions.append({ + "type": session.TAG, + "bitrate": bitrate, + "address": session.player.address + }) + # 计算有效比特率 + for session in playing_sessions: + # 设置了不限速范围则判断session ip是否在不限速范围内 + if self._unlimited_ips["ipv4"] or self._unlimited_ips["ipv6"]: + if not self.__allow_access(self._unlimited_ips, session.get("address")) \ + and session.get("type") == "Video": + total_bit_rate += int(session.get("bitrate") or 0) + # 未设置不限速范围,则默认不限速内网ip + elif not IpUtils.is_private_ip(session.get("address")) \ and session.get("type") == "Video": total_bit_rate += int(session.get("bitrate") or 0) - # 未设置不限速范围,则默认不限速内网ip - elif not IpUtils.is_private_ip(session.get("address")) \ - and session.get("type") == "Video": - total_bit_rate += int(session.get("bitrate") or 0) if total_bit_rate: # 开启智能限速计算上传限速 From cb93a639704ce75cb8500f6fc35ca9e4b3b0ef2b Mon Sep 17 00:00:00 2001 From: jxxghp Date: Wed, 27 Sep 2023 08:16:26 +0800 Subject: [PATCH 3/4] =?UTF-8?q?feat=20=E5=8E=86=E5=8F=B2=E8=AE=B0=E5=BD=95?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E9=87=8D=E6=96=B0=E8=AF=86=E5=88=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/endpoints/history.py | 13 ++++++++----- app/chain/media.py | 10 +++------- app/chain/transfer.py | 21 +++++++++++---------- app/core/metainfo.py | 16 +++++++++++++++- app/plugins/brushflow/__init__.py | 2 +- app/plugins/dirmonitor/__init__.py | 15 +++++---------- 6 files changed, 43 insertions(+), 34 deletions(-) diff --git a/app/api/endpoints/history.py b/app/api/endpoints/history.py index fff975a0..4cd28956 100644 --- a/app/api/endpoints/history.py +++ b/app/api/endpoints/history.py @@ -85,15 +85,18 @@ def delete_transfer_history(history_in: schemas.TransferHistory, @router.post("/transfer", summary="历史记录重新转移", response_model=schemas.Response) def redo_transfer_history(history_in: schemas.TransferHistory, - mtype: str, - new_tmdbid: int, + mtype: str = None, + new_tmdbid: int = None, db: Session = Depends(get_db), _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ - 历史记录重新转移 + 历史记录重新转移,不输入 mtype 和 new_tmdbid 时,自动使用文件名重新识别 """ - state, errmsg = TransferChain(db).re_transfer(logid=history_in.id, - mtype=MediaType(mtype), tmdbid=new_tmdbid) + if mtype and new_tmdbid: + state, errmsg = TransferChain(db).re_transfer(logid=history_in.id, + mtype=MediaType(mtype), tmdbid=new_tmdbid) + else: + state, errmsg = TransferChain(db).re_transfer(logid=history_in.id) if state: return schemas.Response(success=True) else: diff --git a/app/chain/media.py b/app/chain/media.py index 243b7071..e797525f 100644 --- a/app/chain/media.py +++ b/app/chain/media.py @@ -4,7 +4,7 @@ from typing import Optional, List, Tuple from app.chain import ChainBase from app.core.context import Context, MediaInfo from app.core.meta import MetaBase -from app.core.metainfo import MetaInfo +from app.core.metainfo import MetaInfo, MetaInfoPath from app.log import logger from app.utils.string import StringUtils @@ -38,12 +38,8 @@ class MediaChain(ChainBase): """ logger.info(f'开始识别媒体信息,文件:{path} ...') file_path = Path(path) - # 上级目录元数据 - dir_meta = MetaInfo(title=file_path.parent.name) - # 文件元数据,不包含后缀 - file_meta = MetaInfo(title=file_path.stem) - # 合并元数据 - file_meta.merge(dir_meta) + # 元数据 + file_meta = MetaInfoPath(file_path) # 识别媒体信息 mediainfo = self.recognize_media(meta=file_meta) if not mediainfo: diff --git a/app/chain/transfer.py b/app/chain/transfer.py index 5741ea8a..124e966b 100644 --- a/app/chain/transfer.py +++ b/app/chain/transfer.py @@ -12,7 +12,7 @@ from app.chain.media import MediaChain from app.core.config import settings from app.core.context import MediaInfo from app.core.meta import MetaBase -from app.core.metainfo import MetaInfo +from app.core.metainfo import MetaInfoPath from app.db.downloadhistory_oper import DownloadHistoryOper from app.db.models.downloadhistory import DownloadHistory from app.db.models.transferhistory import TransferHistory @@ -202,12 +202,8 @@ class TransferChain(ChainBase): key=ProgressKey.FileTransfer) if not meta: - # 上级目录元数据 - dir_meta = MetaInfo(title=file_path.parent.name) - # 文件元数据,不包含后缀 - file_meta = MetaInfo(title=file_path.stem) - # 合并元数据 - file_meta.merge(dir_meta) + # 文件元数据 + file_meta = MetaInfoPath(file_path) else: file_meta = meta @@ -474,7 +470,8 @@ class TransferChain(ChainBase): text=errmsg, userid=userid)) return - def re_transfer(self, logid: int, mtype: MediaType, tmdbid: int) -> Tuple[bool, str]: + def re_transfer(self, logid: int, + mtype: MediaType = None, tmdbid: int = None) -> Tuple[bool, str]: """ 根据历史记录,重新识别转移,只处理对应的src目录 :param logid: 历史记录ID @@ -492,11 +489,15 @@ class TransferChain(ChainBase): return False, f"源目录不存在:{src_path}" dest_path = Path(history.dest) if history.dest else None # 查询媒体信息 - mediainfo = self.recognize_media(mtype=mtype, tmdbid=tmdbid) + if mtype and tmdbid: + mediainfo = self.recognize_media(mtype=mtype, tmdbid=tmdbid) + else: + meta = MetaInfoPath(src_path) + mediainfo = self.recognize_media(meta=meta) if not mediainfo: return False, f"未识别到媒体信息,类型:{mtype.value},tmdbid:{tmdbid}" # 重新执行转移 - logger.info(f"{mtype.value} {tmdbid} 识别为:{mediainfo.title_year}") + logger.info(f"{src_path.name} 识别为:{mediainfo.title_year}") # 更新媒体图片 self.obtain_images(mediainfo=mediainfo) diff --git a/app/core/metainfo.py b/app/core/metainfo.py index 6a583b92..2ae668f5 100644 --- a/app/core/metainfo.py +++ b/app/core/metainfo.py @@ -9,7 +9,7 @@ from app.core.meta.words import WordsMatcher def MetaInfo(title: str, subtitle: str = None) -> MetaBase: """ - 媒体整理入口,根据名称和副标题,判断是哪种类型的识别,返回对应对象 + 根据标题和副标题识别元数据 :param title: 标题、种子名、文件名 :param subtitle: 副标题、描述 :return: MetaAnime、MetaVideo @@ -33,6 +33,20 @@ def MetaInfo(title: str, subtitle: str = None) -> MetaBase: return meta +def MetaInfoPath(path: Path) -> MetaBase: + """ + 根据路径识别元数据 + :param path: 路径 + """ + # 上级目录元数据 + dir_meta = MetaInfo(title=path.parent.name) + # 文件元数据,不包含后缀 + file_meta = MetaInfo(title=path.stem) + # 合并元数据 + file_meta.merge(dir_meta) + return file_meta + + def is_anime(name: str) -> bool: """ 判断是否为动漫 diff --git a/app/plugins/brushflow/__init__.py b/app/plugins/brushflow/__init__.py index 7fbd174f..63a380c4 100644 --- a/app/plugins/brushflow/__init__.py +++ b/app/plugins/brushflow/__init__.py @@ -43,7 +43,7 @@ class BrushFlow(_PluginBase): # 加载顺序 plugin_order = 21 # 可使用的用户级别 - auth_level = 3 + auth_level = 2 # 私有属性 siteshelper = None diff --git a/app/plugins/dirmonitor/__init__.py b/app/plugins/dirmonitor/__init__.py index ce6e4a89..0264b8fb 100644 --- a/app/plugins/dirmonitor/__init__.py +++ b/app/plugins/dirmonitor/__init__.py @@ -15,7 +15,7 @@ from watchdog.observers.polling import PollingObserver from app.chain.transfer import TransferChain from app.core.config import settings from app.core.context import MediaInfo -from app.core.metainfo import MetaInfo +from app.core.metainfo import MetaInfoPath from app.db.downloadhistory_oper import DownloadHistoryOper from app.db.transferhistory_oper import TransferHistoryOper from app.log import logger @@ -237,13 +237,8 @@ class DirMonitor(_PluginBase): logger.info(f"{event_path} 已整理过") return - # 上级目录元数据 - meta = MetaInfo(title=file_path.parent.name) - # 文件元数据,不包含后缀 - file_meta = MetaInfo(title=file_path.stem) - # 合并元数据 - file_meta.merge(meta) - + # 元数据 + file_meta = MetaInfoPath(file_path) if not file_meta.name: logger.error(f"{file_path.name} 无法识别有效信息") return @@ -343,7 +338,7 @@ class DirMonitor(_PluginBase): } """ # 发送消息汇总 - media_list = self._medias.get(mediainfo.title_year + " " + meta.season) or {} + media_list = self._medias.get(mediainfo.title_year + " " + file_meta.season) or {} if media_list: media_files = media_list.get("files") or [] if media_files: @@ -384,7 +379,7 @@ class DirMonitor(_PluginBase): ], "time": datetime.now() } - self._medias[mediainfo.title_year + " " + meta.season] = media_list + self._medias[mediainfo.title_year + " " + file_meta.season] = media_list # 汇总刷新媒体库 if settings.REFRESH_MEDIASERVER: From 1401ea74ddeb9ee9e009bd3ecee82deb6b83e6f4 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Wed, 27 Sep 2023 08:22:32 +0800 Subject: [PATCH 4/4] =?UTF-8?q?fix=20#667=20=E7=A1=AC=E9=93=BE=E6=8E=A5?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=9E=81=E7=A9=BA=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/utils/system.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/utils/system.py b/app/utils/system.py index c343c194..2c4e13a9 100644 --- a/app/utils/system.py +++ b/app/utils/system.py @@ -61,7 +61,9 @@ class SystemUtils: 移动 """ try: + # 当前目录改名 temp = src.replace(src.parent / dest.name) + # 移动到目标目录 shutil.move(temp, dest) return 0, "" except Exception as err: @@ -74,7 +76,11 @@ class SystemUtils: 硬链接 """ try: - dest.hardlink_to(src) + # link到当前目录并改名 + tmp_path = src.parent / dest.name + tmp_path.hardlink_to(src) + # 移动到目标目录 + shutil.move(tmp_path, dest) return 0, "" except Exception as err: print(str(err))