Merge pull request #96 from thsrite/main

This commit is contained in:
jxxghp 2023-08-12 22:54:46 +08:00 committed by GitHub
commit 989736dcaf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 224 additions and 26 deletions

View File

@ -2,6 +2,7 @@ import gc
import pickle
import traceback
from abc import ABCMeta
from msilib.schema import File
from pathlib import Path
from typing import Optional, Any, Tuple, List, Set, Union, Dict
@ -307,6 +308,14 @@ class ChainBase(metaclass=ABCMeta):
"""
return self.run_module("stop_torrents", hashs=hashs)
def torrent_files(self, tid: str) -> Optional[List[File]]:
"""
根据种子文件选择并添加下载任务
:param tid: 种子Hash
:return: 种子文件
"""
return self.run_module("torrent_files", tid=tid)
def media_exists(self, mediainfo: MediaInfo, itemid: str = None) -> Optional[ExistMediaInfo]:
"""
判断媒体文件是否存在

View File

@ -1,4 +1,5 @@
import re
from msilib.schema import File
from pathlib import Path
from typing import List, Optional, Tuple, Set, Dict, Union
@ -623,3 +624,13 @@ class DownloadChain(ChainBase):
删除下载任务
"""
return self.remove_torrents(hashs=[hash_str])
def get_files(self, tid: str) -> Optional[List[File]]:
"""
获取种子文件清单
"""
try:
return self.torrent_files(tid=tid)
except Exception as err:
logger.error(f"获取种子文件列表出错:{err}")
return None

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

@ -1,3 +1,4 @@
from msilib.schema import File
from pathlib import Path
from typing import Set, Tuple, Optional, Union, List
@ -187,6 +188,12 @@ class QbittorrentModule(_ModuleBase):
"""
return self.qbittorrent.start_torrents(ids=hashs)
def torrent_files(self, tid: str) -> Optional[List[File]]:
"""
获取种子文件列表
"""
return self.qbittorrent.get_files(tid=tid)
def downloader_info(self) -> schemas.DownloaderInfo:
"""
下载器信息

View File

@ -1,8 +1,9 @@
import time
from msilib.schema import File
from typing import Optional, Union, Tuple, List
import qbittorrentapi
from qbittorrentapi import TorrentFilesList, TorrentDictionary
from qbittorrentapi import TorrentDictionary
from qbittorrentapi.client import Client
from qbittorrentapi.transfer import TransferInfoDictionary
@ -265,7 +266,7 @@ class Qbittorrent(metaclass=Singleton):
logger.error(f"删除种子出错:{err}")
return False
def get_files(self, tid: str) -> Optional[TorrentFilesList]:
def get_files(self, tid: str) -> Optional[List[File]]:
"""
获取种子文件清单
"""

View File

@ -1,3 +1,4 @@
from msilib.schema import File
from pathlib import Path
from typing import Set, Tuple, Optional, Union, List
@ -171,6 +172,12 @@ class TransmissionModule(_ModuleBase):
"""
return self.transmission.start_torrents(ids=hashs)
def torrent_files(self, tid: str) -> Optional[List[File]]:
"""
获取种子文件列表
"""
return self.transmission.get_files(tid=tid)
def downloader_info(self) -> schemas.DownloaderInfo:
"""
下载器信息

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,24 +480,20 @@ class MediaSyncDel(_PluginBase):
return
# 开始删除
del_cnt = 0
image = 'https://emby.media/notificationicon.png'
year = None
for transferhis in transfer_history:
image = transferhis.image
year = transferhis.year
if media_type == "Episode" or media_type == "Movie":
# 如果有剧集或者电影有多个版本的话,需要根据名称筛选下要删除的版本
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 +521,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()))}"
)
@ -616,17 +609,14 @@ class MediaSyncDel(_PluginBase):
transfer_history: List[TransferHistory] = self._transferhis.get_by(
mtype="电视剧",
title=media_name,
year=media_year,
season=media_season)
year=media_year)
# 删除剧集S02E02
elif media_type == "Episode":
msg = f'剧集 {media_name} {media_season}{media_episode}'
transfer_history: List[TransferHistory] = self._transferhis.get_by(
mtype="电视剧",
title=media_name,
year=media_year,
season=media_season,
episode=media_episode)
year=media_year)
else:
continue
@ -648,7 +638,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 +681,149 @@ 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))
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}"