feat 同步删除处理转种辅种、种子部分删除自动暂停

This commit is contained in:
thsrite 2023-08-12 22:16:53 +08:00
parent c2ae88a448
commit 7b980f2a07
6 changed files with 206 additions and 15 deletions

View File

@ -307,6 +307,14 @@ class ChainBase(metaclass=ABCMeta):
"""
return self.run_module("stop_torrents", hashs=hashs)
def get_torrent_files(self, tid: str) -> Optional[Tuple[Optional[str], str]]:
"""
根据种子文件选择并添加下载任务
:param tid: 种子Hash
:return: 种子文件
"""
return self.run_module("get_files", tid=tid)
def media_exists(self, mediainfo: MediaInfo, itemid: str = None) -> Optional[ExistMediaInfo]:
"""
判断媒体文件是否存在

View File

@ -2,6 +2,8 @@ import re
from pathlib import Path
from typing import List, Optional, Tuple, Set, Dict, Union
from qbittorrentapi import TorrentFilesList
from app.chain import ChainBase
from app.core.config import settings
from app.core.context import MediaInfo, TorrentInfo, Context
@ -623,3 +625,13 @@ class DownloadChain(ChainBase):
删除下载任务
"""
return self.remove_torrents(hashs=[hash_str])
def get_files(self, tid: str) -> Optional[TorrentFilesList]:
"""
获取种子文件清单
"""
try:
return self.get_torrent_files(tid=tid)
except Exception as err:
logger.error(f"获取种子文件列表出错:{err}")
return []

View File

@ -20,3 +20,7 @@ class PluginData(Base):
@staticmethod
def get_plugin_data_by_key(db: Session, plugin_id: str, key: str):
return db.query(PluginData).filter(PluginData.plugin_id == plugin_id, PluginData.key == key).first()
@staticmethod
def del_plugin_data_by_key(db: Session, plugin_id: str, key: str):
return db.query(PluginData).filter(PluginData.plugin_id == plugin_id, PluginData.key == key).delete()

View File

@ -44,6 +44,14 @@ class PluginDataOper(DbOper):
return json.loads(data.value)
return data.value
def del_data(self, plugin_id: str, key: str) -> Any:
"""
删除插件数据
:param plugin_id: 插件id
:param key: 数据key
"""
PluginData.del_plugin_data_by_key(self._db, plugin_id, key)
def truncate(self):
"""
清空插件数据

View File

@ -142,20 +142,35 @@ class _PluginBase(metaclass=ABCMeta):
data_path.mkdir(parents=True)
return data_path
def save_data(self, key: str, value: Any) -> Base:
def save_data(self, key: str, value: Any, plugin_id: str = None) -> Base:
"""
保存插件数据
:param key: 数据key
:param value: 数据值
"""
return self.plugindata.save(self.__class__.__name__, key, value)
if not plugin_id:
plugin_id = self.__class__.__name__
return self.plugindata.save(plugin_id, key, value)
def get_data(self, key: str) -> Any:
def get_data(self, key: str, plugin_id: str = None) -> Any:
"""
获取插件数据
:param key: 数据key
:param plugin_id: plugin_id
"""
return self.plugindata.get_data(self.__class__.__name__, key)
if not plugin_id:
plugin_id = self.__class__.__name__
return self.plugindata.get_data(plugin_id, key)
def del_data(self, key: str, plugin_id: str = None) -> Any:
"""
删除插件数据
:param key: 数据key
:param plugin_id: plugin_id
"""
if not plugin_id:
plugin_id = self.__class__.__name__
return self.plugindata.del_data(plugin_id, key)
def post_message(self, channel: MessageChannel = None, mtype: NotificationType = None, title: str = None,
text: str = None, image: str = None, link: str = None, userid: str = None):

View File

@ -4,6 +4,7 @@ import os
import re
import shutil
import time
from pathlib import Path
from typing import List, Tuple, Dict, Any, Optional
from apscheduler.schedulers.background import BackgroundScheduler
@ -461,17 +462,14 @@ class MediaSyncDel(_PluginBase):
logger.error(f"{media_name} 季同步删除失败,未获取到具体季")
return
msg = f'剧集 {media_name} S{season_num} {tmdb_id}'
transfer_history: List[TransferHistory] = self._transferhis.get_by(tmdbid=tmdb_id,
season=f"S{season_num}")
transfer_history: List[TransferHistory] = self._transferhis.get_by(tmdbid=tmdb_id)
# 删除剧集S02E02
elif media_type == "Episode":
if not season_num or not str(season_num).isdigit() or not episode_num or not str(episode_num).isdigit():
logger.error(f"{media_name} 集同步删除失败,未获取到具体集")
return
msg = f'剧集 {media_name} S{season_num}E{episode_num} {tmdb_id}'
transfer_history: List[TransferHistory] = self._transferhis.get_by(tmdbid=tmdb_id,
season=f"S{season_num}",
episode=f"E{episode_num}")
transfer_history: List[TransferHistory] = self._transferhis.get_by(tmdbid=tmdb_id)
else:
return
@ -482,7 +480,6 @@ class MediaSyncDel(_PluginBase):
return
# 开始删除
del_cnt = 0
image = 'https://emby.media/notificationicon.png'
year = None
for transferhis in transfer_history:
@ -492,14 +489,15 @@ class MediaSyncDel(_PluginBase):
# 如果有剧集或者电影有多个版本的话,需要根据名称筛选下要删除的版本
if os.path.basename(transferhis.dest) != os.path.basename(media_path):
continue
self._transferhis.delete(transferhis.id)
del_cnt += 1
# 删除种子任务
if self._del_source:
del_source = False
if transferhis.download_hash:
try:
self.chain.remove_torrents(transferhis.download_hash)
# 判断种子是否被删除完
self.handle_torrent(history_id=transferhis.id,
src=history.src,
torrent_hash=history.download_hash)
except Exception as e:
logger.error("删除种子失败,尝试删除源文件:%s" % str(e))
del_source = True
@ -527,7 +525,6 @@ class MediaSyncDel(_PluginBase):
title="媒体库同步删除任务完成",
image=image,
text=f"{msg}\n"
f"数量 {del_cnt}\n"
f"时间 {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))}"
)
@ -648,7 +645,10 @@ class MediaSyncDel(_PluginBase):
del_source = False
if transferhis.download_hash:
try:
self.chain.remove_torrents(transferhis.download_hash)
# 判断种子是否被删除完
self.handle_torrent(history_id=transferhis.id,
src=history.src,
torrent_hash=history.download_hash)
except Exception as e:
logger.error("删除种子失败,尝试删除源文件:%s" % str(e))
del_source = True
@ -688,6 +688,150 @@ class MediaSyncDel(_PluginBase):
self.save_data("last_time", datetime.datetime.now())
def handle_torrent(self, history_id: int, src: str, torrent_hash: str):
"""
判断种子是否局部删除
局部删除则暂停种子
全部删除则删除种子
"""
download_id = torrent_hash
download = settings.DOWNLOADER
history_key = "%s-%s" % (download, torrent_hash)
plugin_id = "TorrentTransfer"
transfer_history = self.get_data(key=history_key,
plugin_id=plugin_id)
logger.info(f"查询到 {history_key} 转种历史 {transfer_history}")
# 删除历史标志
del_history = False
# 删除种子标志
delete_flag = True
# 是否需要暂停源下载器种子
stop_from = False
# 如果有转种记录,则删除转种后的下载任务
if transfer_history and isinstance(transfer_history, dict):
download = transfer_history['to_download']
download_id = transfer_history['to_download_id']
delete_source = transfer_history['delete_source']
del_history = True
# 转种后未删除源种时,同步删除源种
if not delete_source:
logger.info(f"{history_key} 转种时未删除源下载任务,开始删除源下载任务…")
try:
dl_files = self.chain.get_files(tid=torrent_hash)
if not dl_files:
logger.info(f"未获取到 {settings.DOWNLOADER} - {torrent_hash} 种子文件,种子已被删除")
else:
for dl_file in dl_files:
dl_file_name = dl_file.get("name")
torrent_file = os.path.join(src, os.path.basename(dl_file_name))
if Path(torrent_file).exists():
logger.warn(f"种子有文件被删除,种子文件{torrent_file}暂未删除,暂停种子")
delete_flag = False
stop_from = True
break
if delete_flag:
logger.info(f"删除下载任务:{settings.DOWNLOADER} - {torrent_hash}")
self.chain.remove_torrents(torrent_hash)
except Exception as e:
logger.error(f"删除源下载任务 {history_key} 失败: {str(e)}")
# 如果是False则说明种子文件没有完全被删除暂停种子暂不处理
if delete_flag:
try:
dl_files = self.chain.get_files(tid=download_id)
if not dl_files:
logger.info(f"未获取到 {download} - {download_id} 种子文件,种子已被删除")
else:
for dl_file in dl_files:
dl_file_name = dl_file.get("name")
if not stop_from:
torrent_file = os.path.join(src, os.path.basename(dl_file_name))
logger.error(f"种子路径 {torrent_file} {Path(torrent_file).exists()}")
if Path(torrent_file).exists():
logger.info(f"种子有文件被删除,种子文件{torrent_file}暂未删除,暂停种子")
delete_flag = False
break
if delete_flag:
# 删除源下载任务或转种后下载任务
logger.info(f"删除下载任务:{download} - {download_id}")
self.chain.remove_torrents(download_id)
# 删除转移记录
self._transferhis.delete(history_id)
# 删除转种记录
if del_history:
self.del_data(key=history_key, plugin_id=plugin_id)
# 处理辅种
self.__del_seed(download=download, download_id=download_id, action_flag="del")
except Exception as e:
logger.error(f"删除转种辅种下载任务失败: {str(e)}")
# 判断是否暂停
if not delete_flag:
logger.error("开始暂停种子")
# 暂停种子
if stop_from:
# 暂停源种
self.chain.stop_torrents(torrent_hash)
logger.info(f"种子:{settings.DOWNLOADER} - {torrent_hash} 暂停")
# 转种
self.chain.stop_torrents(download_id)
logger.info(f"转种:{download} - {download_id} 暂停")
# 辅种
self.__del_seed(download=download, download_id=download_id, action_flag="stop")
def __del_seed(self, download, download_id, action_flag):
"""
删除辅种
"""
# 查询是否有辅种记录
history_key = download_id
plugin_id = "IYUUAutoSeed"
seed_history = self.get_data(key=history_key,
plugin_id=plugin_id) or []
logger.info(f"查询到 {history_key} 辅种历史 {seed_history}")
# 有辅种记录则处理辅种
if seed_history and isinstance(seed_history, list):
for history in seed_history:
downloader = history['downloader']
torrents = history['torrents']
if not downloader or not torrents:
return
if not isinstance(torrents, list):
torrents = [torrents]
# 删除辅种历史中与本下载器相同的辅种记录
if int(downloader) == download:
for torrent in torrents:
# 删除辅种
if action_flag == "del":
logger.info(f"删除辅种:{downloader} - {torrent}")
self.chain.remove_torrents(torrent)
# 暂停辅种
if action_flag == "stop":
self.chain.stop_torrents(torrent)
logger.info(f"辅种:{downloader} - {torrent} 暂停")
# 删除本下载器辅种历史
if action_flag == "del":
del history
break
# 更新辅种历史
self.save_data(key=history_key,
value=seed_history,
plugin_id=plugin_id)
@staticmethod
def parse_emby_log(last_time):
log_url = "{HOST}System/Logs/embyserver.txt?api_key={APIKEY}"