mirror of
https://github.com/EstrellaXD/Auto_Bangumi.git
synced 2026-11-01 21:26:08 +08:00
fix(renamer): distinct names for multi-file movies, revert group-tag prefix, season floor
- Multi-file movie torrents: the largest file keeps "Title (Year).ext", extras get "Title (Year) - <original-stem>.ext" (idempotent across rename cycles) — no more per-cycle qB conflicts or aria2 collisions - Reverted the "[group] " filename prefix: group_tag historically only named the qB RSS rule; prefixing renames would mass-rename existing seeding libraries on upgrade - Season floor restored for regular episodes (season+offset<1 reverts to the original season); only specials may land in Season 0 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GTnuEgR78cBk32bLNKar3z
This commit is contained in:
@@ -12,6 +12,9 @@
|
||||
### Fixed
|
||||
|
||||
- 修复 `gen_save_path` 季度守卫的一处预置 off-by-one 缺陷:合法的 `season=0`(特别篇)此前会被误判并回退为原值并打印多余警告
|
||||
- 收紧 `gen_save_path` 季度守卫:普通剧集在 `season_offset` 为负导致季号落到 0 时回退原季号(Season 0 会被 Plex/Jellyfin 当作特别篇),仅 `special` 类型允许合法落入第 0 季
|
||||
- 撤销 `gen_path` 在重命名文件名前追加 `[字幕组] ` 前缀的改动(`group_tag` 恢复为仅影响 qBittorrent RSS 规则名):避免已开启 `group_tag` 的用户升级后整个做种媒体库被批量重命名,破坏 Plex/Jellyfin 索引与 cross-seed/硬链接
|
||||
- 修复多文件电影种子(正片 + 特典/花絮,BD 常见)所有文件生成相同目标文件名导致重命名互相冲突/覆盖的问题:体积最大的主文件使用「标题 (年份).ext」干净名,其余文件追加原始文件名词干作区分
|
||||
|
||||
## Frontend
|
||||
|
||||
|
||||
@@ -81,8 +81,11 @@ def gen_save_path(data: Bangumi | BangumiUpdate):
|
||||
return str(Path(settings.downloader.path) / folder)
|
||||
# Apply season_offset to get the adjusted season number for the folder
|
||||
adjusted_season = data.season + getattr(data, "season_offset", 0)
|
||||
if adjusted_season < 0:
|
||||
adjusted_season = data.season # Safety: don't go below 0 (0 = specials)
|
||||
# 季号下限:普通剧集最小为 1——偏移到 Season 0 会被 Plex/Jellyfin 当作
|
||||
# 特别篇;只有特别篇(special)允许合法落入第 0 季
|
||||
min_season = 0 if episode_type == "special" else 1
|
||||
if adjusted_season < min_season:
|
||||
adjusted_season = data.season
|
||||
logger.warning(
|
||||
f"[Path] Season offset would result in invalid season for {data.official_title}, using original season"
|
||||
)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import asyncio
|
||||
import logging
|
||||
import time
|
||||
from pathlib import PurePath
|
||||
|
||||
from module.conf import settings
|
||||
from module.database import Database
|
||||
@@ -79,13 +80,9 @@ class Renamer:
|
||||
f"[Renamer] Episode offset {episode_offset} would make episode {original_episode} non-positive, ignoring offset"
|
||||
)
|
||||
episode = f"0{adjusted_episode}" if adjusted_episode < 10 else adjusted_episode
|
||||
# 与旧版 downloader/path.py 的 rule_name 行为保持一致:开启 group_tag 时,
|
||||
# 在文件名前加上 "[字幕组] " 前缀
|
||||
group_prefix = (
|
||||
f"[{file_info.group}] "
|
||||
if settings.bangumi_manage.group_tag and file_info.group
|
||||
else ""
|
||||
)
|
||||
# 注意:group_tag 只影响 qB RSS 规则名(downloader/path.py 的 rule_name),
|
||||
# 从不写进重命名后的文件名——已有做种媒体库的文件名必须保持稳定,
|
||||
# 否则升级后会触发整库批量重命名,破坏 Plex/Jellyfin 索引与硬链接
|
||||
if method == "none" or method == "subtitle_none":
|
||||
return file_info.media_path
|
||||
if file_info.episode_type == "movie":
|
||||
@@ -96,14 +93,12 @@ class Renamer:
|
||||
assert isinstance(
|
||||
file_info, SubtitleFile
|
||||
), "subtitle methods require a SubtitleFile"
|
||||
return f"{group_prefix}{base}.{file_info.language}{file_info.suffix}"
|
||||
return f"{group_prefix}{base}{file_info.suffix}"
|
||||
return f"{base}.{file_info.language}{file_info.suffix}"
|
||||
return f"{base}{file_info.suffix}"
|
||||
elif method == "pn":
|
||||
return (
|
||||
f"{group_prefix}{file_info.title} S{season}E{episode}{file_info.suffix}"
|
||||
)
|
||||
return f"{file_info.title} S{season}E{episode}{file_info.suffix}"
|
||||
elif method == "advance":
|
||||
return f"{group_prefix}{bangumi_name} S{season}E{episode}{file_info.suffix}"
|
||||
return f"{bangumi_name} S{season}E{episode}{file_info.suffix}"
|
||||
elif method == "normal":
|
||||
logger.warning("[Renamer] Normal rename method is deprecated.")
|
||||
return file_info.media_path
|
||||
@@ -111,12 +106,12 @@ class Renamer:
|
||||
assert isinstance(
|
||||
file_info, SubtitleFile
|
||||
), "subtitle_pn requires a SubtitleFile"
|
||||
return f"{group_prefix}{file_info.title} S{season}E{episode}.{file_info.language}{file_info.suffix}"
|
||||
return f"{file_info.title} S{season}E{episode}.{file_info.language}{file_info.suffix}"
|
||||
elif method == "subtitle_advance":
|
||||
assert isinstance(
|
||||
file_info, SubtitleFile
|
||||
), "subtitle_advance requires a SubtitleFile"
|
||||
return f"{group_prefix}{bangumi_name} S{season}E{episode}.{file_info.language}{file_info.suffix}"
|
||||
return f"{bangumi_name} S{season}E{episode}.{file_info.language}{file_info.suffix}"
|
||||
else:
|
||||
logger.error(f"[Renamer] Unknown rename method: {method}")
|
||||
return file_info.media_path
|
||||
@@ -195,6 +190,19 @@ class Renamer:
|
||||
await self.client.delete_torrent(hashes=_hash)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _gen_movie_extra_path(new_path: str, media_path: str) -> str:
|
||||
"""多文件电影种子中,非主文件在干净名(Title (Year).ext)基础上追加
|
||||
原始文件名词干作区分,避免与主文件生成相同目标名互相冲突/覆盖;
|
||||
词干若已带 "Title (Year) - " 前缀则先剥离,保证重命名幂等。"""
|
||||
suffix = PurePath(new_path).suffix
|
||||
base = new_path[: -len(suffix)] if suffix else new_path
|
||||
stem = PurePath(media_path).stem
|
||||
prefix = f"{base} - "
|
||||
if stem.startswith(prefix):
|
||||
stem = stem[len(prefix) :]
|
||||
return f"{base} - {stem}{suffix}"
|
||||
|
||||
async def rename_collection(
|
||||
self,
|
||||
media_list: list[str],
|
||||
@@ -205,8 +213,19 @@ class Renamer:
|
||||
episode_offset: int = 0,
|
||||
season_offset: int = 0,
|
||||
episode_type: str = "episode",
|
||||
file_sizes: dict[str, int] | None = None,
|
||||
**kwargs,
|
||||
):
|
||||
# 多文件电影种子(正片 + 特典/花絮):所有文件会解析出同一标题,
|
||||
# 需选出主文件(体积最大者,无体积信息时取首个),其余文件追加区分词干
|
||||
movie_primary: str | None = None
|
||||
if episode_type == "movie":
|
||||
ep_list = [p for p in media_list if is_ep(p)]
|
||||
if ep_list:
|
||||
if file_sizes:
|
||||
movie_primary = max(ep_list, key=lambda p: file_sizes.get(p, 0))
|
||||
else:
|
||||
movie_primary = ep_list[0]
|
||||
for media_path in media_list:
|
||||
if is_ep(media_path):
|
||||
ep = self._parser.torrent_parser(
|
||||
@@ -222,6 +241,13 @@ class Renamer:
|
||||
episode_offset=episode_offset,
|
||||
season_offset=season_offset,
|
||||
)
|
||||
if (
|
||||
movie_primary is not None
|
||||
and media_path != movie_primary
|
||||
and new_path != media_path
|
||||
):
|
||||
# new_path == media_path 说明是 none 等直通方法,不做区分
|
||||
new_path = self._gen_movie_extra_path(new_path, media_path)
|
||||
if media_path != new_path:
|
||||
renamed = await self.client.rename_torrent_file(
|
||||
_hash=_hash, old_path=media_path, new_path=new_path
|
||||
@@ -543,7 +569,11 @@ class Renamer:
|
||||
# Rename collection
|
||||
elif len(media_list) > 1:
|
||||
logger.info("[Renamer] Start rename collection")
|
||||
await self.rename_collection(media_list=media_list, **kwargs)
|
||||
# 传入各文件体积,供多文件电影种子选出正片主文件
|
||||
file_sizes = {f["name"]: f.get("size") or 0 for f in files}
|
||||
await self.rename_collection(
|
||||
media_list=media_list, file_sizes=file_sizes, **kwargs
|
||||
)
|
||||
if len(subtitle_list) > 0:
|
||||
await self.rename_subtitles(subtitle_list=subtitle_list, **kwargs)
|
||||
await self.client.set_category(torrent_hash, "BangumiCollection")
|
||||
|
||||
@@ -82,6 +82,52 @@ class TestGenSavePath:
|
||||
|
||||
assert result == "/downloads/Bangumi/My Anime (2024)/Season 0"
|
||||
|
||||
def test_gen_save_path_regular_offset_to_zero_reverts_to_original_season(self):
|
||||
"""普通剧集 season+offset 落到 0 时回退原季号:Season 0 会被
|
||||
Plex/Jellyfin 当作特别篇,只有 special 类型才允许落入。"""
|
||||
bangumi = make_bangumi(
|
||||
official_title="My Anime",
|
||||
year="2024",
|
||||
season=1,
|
||||
season_offset=-1,
|
||||
episode_type="episode",
|
||||
)
|
||||
with patch("module.downloader.path.settings") as mock_settings:
|
||||
mock_settings.downloader.path = "/downloads/Bangumi"
|
||||
result = gen_save_path(bangumi)
|
||||
|
||||
assert result == "/downloads/Bangumi/My Anime (2024)/Season 1"
|
||||
|
||||
def test_gen_save_path_special_offset_to_zero_lands_in_season_zero(self):
|
||||
"""特别篇(special)经偏移落到第 0 季是合法的(Jellyfin/Plex 惯例)。"""
|
||||
bangumi = make_bangumi(
|
||||
official_title="My Anime",
|
||||
year="2024",
|
||||
season=1,
|
||||
season_offset=-1,
|
||||
episode_type="special",
|
||||
)
|
||||
with patch("module.downloader.path.settings") as mock_settings:
|
||||
mock_settings.downloader.path = "/downloads/Bangumi"
|
||||
result = gen_save_path(bangumi)
|
||||
|
||||
assert result == "/downloads/Bangumi/My Anime (2024)/Season 0"
|
||||
|
||||
def test_gen_save_path_special_offset_below_zero_reverts_to_original_season(self):
|
||||
"""特别篇偏移到负季号仍属非法配置,回退原季号。"""
|
||||
bangumi = make_bangumi(
|
||||
official_title="My Anime",
|
||||
year="2024",
|
||||
season=0,
|
||||
season_offset=-1,
|
||||
episode_type="special",
|
||||
)
|
||||
with patch("module.downloader.path.settings") as mock_settings:
|
||||
mock_settings.downloader.path = "/downloads/Bangumi"
|
||||
result = gen_save_path(bangumi)
|
||||
|
||||
assert result == "/downloads/Bangumi/My Anime (2024)/Season 0"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# rule_name
|
||||
|
||||
@@ -161,7 +161,8 @@ class TestGenPathMovie:
|
||||
result = Renamer.gen_path(sub, "天气之子 (2019)", method="subtitle_advance")
|
||||
assert result == "天气之子 (2019).zh.ass"
|
||||
|
||||
def test_group_tag_prefix_applied(self):
|
||||
def test_gen_path_movie_group_tag_enabled_keeps_clean_name(self):
|
||||
"""group_tag 不影响重命名文件名(只影响 qB RSS 规则名)。"""
|
||||
ep = EpisodeFile(
|
||||
media_path="raw.mkv",
|
||||
group="Lilith-Raws",
|
||||
@@ -173,7 +174,7 @@ class TestGenPathMovie:
|
||||
)
|
||||
with patch.object(settings.bangumi_manage, "group_tag", True):
|
||||
result = Renamer.gen_path(ep, "天气之子 (2019)", method="advance")
|
||||
assert result == "[Lilith-Raws] 天气之子 (2019).mkv"
|
||||
assert result == "天气之子 (2019).mkv"
|
||||
|
||||
def test_none_method_returns_original_path(self):
|
||||
ep = EpisodeFile(
|
||||
@@ -193,12 +194,13 @@ class TestGenPathMovie:
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestGenPathWithGroupTag:
|
||||
"""gen_path honors bangumi_manage.group_tag as a "[Group] " prefix,
|
||||
mirroring the legacy downloader/path.py rule_name behavior."""
|
||||
class TestGenPathGroupTagStability:
|
||||
"""group_tag 只影响 qB RSS 规则名(downloader/path.py 的 rule_name),
|
||||
从不写进重命名后的文件名:升级后已有做种媒体库的文件名必须保持稳定,
|
||||
否则会触发整库批量重命名,破坏 Plex/Jellyfin 索引与 cross-seed/硬链接。"""
|
||||
|
||||
def test_group_tag_enabled_prefixes_pn_method(self):
|
||||
"""pn method gets a "[Group] " prefix when group_tag is enabled."""
|
||||
def test_gen_path_group_tag_enabled_pn_no_prefix(self):
|
||||
"""pn 方法在 group_tag 开启时也不加 "[Group] " 前缀。"""
|
||||
ep = EpisodeFile(
|
||||
media_path="old.mkv",
|
||||
group="SubGroup",
|
||||
@@ -209,10 +211,10 @@ class TestGenPathWithGroupTag:
|
||||
)
|
||||
with patch.object(settings.bangumi_manage, "group_tag", True):
|
||||
result = Renamer.gen_path(ep, "Bangumi Name", method="pn")
|
||||
assert result == "[SubGroup] My Anime S01E05.mkv"
|
||||
assert result == "My Anime S01E05.mkv"
|
||||
|
||||
def test_group_tag_enabled_prefixes_advance_method(self):
|
||||
"""advance method gets a "[Group] " prefix when group_tag is enabled."""
|
||||
def test_gen_path_group_tag_enabled_advance_no_prefix(self):
|
||||
"""advance 方法在 group_tag 开启时也不加前缀。"""
|
||||
ep = EpisodeFile(
|
||||
media_path="old.mkv",
|
||||
group="SubGroup",
|
||||
@@ -223,10 +225,10 @@ class TestGenPathWithGroupTag:
|
||||
)
|
||||
with patch.object(settings.bangumi_manage, "group_tag", True):
|
||||
result = Renamer.gen_path(ep, "Bangumi Name", method="advance")
|
||||
assert result == "[SubGroup] Bangumi Name S01E05.mkv"
|
||||
assert result == "Bangumi Name S01E05.mkv"
|
||||
|
||||
def test_group_tag_enabled_prefixes_subtitle_methods(self):
|
||||
"""subtitle_pn/subtitle_advance also get the prefix when enabled."""
|
||||
def test_gen_path_group_tag_enabled_subtitle_methods_no_prefix(self):
|
||||
"""subtitle_pn/subtitle_advance 在 group_tag 开启时也不加前缀。"""
|
||||
sub = SubtitleFile(
|
||||
media_path="sub.ass",
|
||||
group="SubGroup",
|
||||
@@ -238,10 +240,10 @@ class TestGenPathWithGroupTag:
|
||||
)
|
||||
with patch.object(settings.bangumi_manage, "group_tag", True):
|
||||
result = Renamer.gen_path(sub, "Bangumi Name", method="subtitle_pn")
|
||||
assert result == "[SubGroup] My Anime S01E05.zh.ass"
|
||||
assert result == "My Anime S01E05.zh.ass"
|
||||
|
||||
def test_group_tag_disabled_no_prefix(self):
|
||||
"""No prefix is added when group_tag is disabled, even with a group."""
|
||||
def test_gen_path_group_tag_disabled_no_prefix(self):
|
||||
"""group_tag 关闭时同样没有前缀。"""
|
||||
ep = EpisodeFile(
|
||||
media_path="old.mkv",
|
||||
group="SubGroup",
|
||||
@@ -254,24 +256,8 @@ class TestGenPathWithGroupTag:
|
||||
result = Renamer.gen_path(ep, "Bangumi Name", method="pn")
|
||||
assert result == "My Anime S01E05.mkv"
|
||||
|
||||
def test_group_tag_enabled_but_no_group_no_prefix(self):
|
||||
"""No "[None] " prefix leaks in when group_tag is enabled but the
|
||||
parsed file has no group (e.g. no [Group] tag in the torrent name)."""
|
||||
ep = EpisodeFile(
|
||||
media_path="old.mkv",
|
||||
group=None,
|
||||
title="My Anime",
|
||||
season=1,
|
||||
episode=5,
|
||||
suffix=".mkv",
|
||||
)
|
||||
with patch.object(settings.bangumi_manage, "group_tag", True):
|
||||
result = Renamer.gen_path(ep, "Bangumi Name", method="pn")
|
||||
assert result == "My Anime S01E05.mkv"
|
||||
|
||||
def test_group_tag_enabled_none_and_subtitle_none_unaffected(self):
|
||||
"""none/subtitle_none methods return the original path unchanged,
|
||||
regardless of group_tag."""
|
||||
def test_gen_path_group_tag_enabled_none_method_returns_original(self):
|
||||
"""none/subtitle_none 方法始终原样返回路径,与 group_tag 无关。"""
|
||||
ep = EpisodeFile(
|
||||
media_path="original/path/file.mkv",
|
||||
group="SubGroup",
|
||||
@@ -467,6 +453,81 @@ class TestRenameCollection:
|
||||
# Only called once for ep01.mkv (depth 1)
|
||||
assert renamer.client.client.torrents_rename_file.call_count == 1
|
||||
|
||||
@staticmethod
|
||||
def _rename_new_paths(mock_client) -> list[str]:
|
||||
"""提取 torrents_rename_file 每次调用的 new_path 参数。"""
|
||||
return [
|
||||
call.kwargs["new_path"]
|
||||
for call in mock_client.torrents_rename_file.call_args_list
|
||||
]
|
||||
|
||||
async def test_rename_collection_movie_multifile_targets_distinct(self, renamer):
|
||||
"""多文件电影种子(正片 + 特典)不能全部生成同一个目标文件名:
|
||||
主文件用干净名,其余文件追加原始文件名词干作区分。"""
|
||||
media_list = ["Tenki no Ko.mkv", "Tenki no Ko Extra PV.mkv"]
|
||||
renamer.client.client.torrents_rename_file.return_value = True
|
||||
await renamer.rename_collection(
|
||||
media_list=media_list,
|
||||
bangumi_name="天气之子 (2019)",
|
||||
season=1,
|
||||
method="advance",
|
||||
_hash="hash_movie",
|
||||
episode_type="movie",
|
||||
file_sizes={
|
||||
"Tenki no Ko.mkv": 8_000_000_000,
|
||||
"Tenki no Ko Extra PV.mkv": 100_000_000,
|
||||
},
|
||||
)
|
||||
|
||||
new_paths = self._rename_new_paths(renamer.client.client)
|
||||
assert len(new_paths) == 2
|
||||
assert len(set(new_paths)) == 2
|
||||
assert "天气之子 (2019).mkv" in new_paths
|
||||
|
||||
async def test_rename_collection_movie_largest_file_gets_clean_name(self, renamer):
|
||||
"""体积最大的文件是主文件,即使它不在列表首位。"""
|
||||
media_list = ["Extras Menu.mkv", "Tenki no Ko Main Feature.mkv"]
|
||||
renamer.client.client.torrents_rename_file.return_value = True
|
||||
await renamer.rename_collection(
|
||||
media_list=media_list,
|
||||
bangumi_name="天气之子 (2019)",
|
||||
season=1,
|
||||
method="advance",
|
||||
_hash="hash_movie",
|
||||
episode_type="movie",
|
||||
file_sizes={
|
||||
"Extras Menu.mkv": 100_000_000,
|
||||
"Tenki no Ko Main Feature.mkv": 8_000_000_000,
|
||||
},
|
||||
)
|
||||
|
||||
calls = renamer.client.client.torrents_rename_file.call_args_list
|
||||
by_old = {c.kwargs["old_path"]: c.kwargs["new_path"] for c in calls}
|
||||
assert by_old["Tenki no Ko Main Feature.mkv"] == "天气之子 (2019).mkv"
|
||||
assert by_old["Extras Menu.mkv"] == "天气之子 (2019) - Extras Menu.mkv"
|
||||
|
||||
async def test_rename_collection_movie_already_renamed_skips_rename(self, renamer):
|
||||
"""区分名是幂等的:已重命名过的多文件电影种子下一轮不再触发重命名。"""
|
||||
media_list = [
|
||||
"天气之子 (2019).mkv",
|
||||
"天气之子 (2019) - Tenki no Ko Extra PV.mkv",
|
||||
]
|
||||
renamer.client.client.torrents_rename_file.return_value = True
|
||||
await renamer.rename_collection(
|
||||
media_list=media_list,
|
||||
bangumi_name="天气之子 (2019)",
|
||||
season=1,
|
||||
method="advance",
|
||||
_hash="hash_movie",
|
||||
episode_type="movie",
|
||||
file_sizes={
|
||||
"天气之子 (2019).mkv": 8_000_000_000,
|
||||
"天气之子 (2019) - Tenki no Ko Extra PV.mkv": 100_000_000,
|
||||
},
|
||||
)
|
||||
|
||||
renamer.client.client.torrents_rename_file.assert_not_called()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# rename_subtitles
|
||||
@@ -618,6 +679,39 @@ class TestRenameFlow:
|
||||
"h1", "BangumiCollection"
|
||||
)
|
||||
|
||||
async def test_rename_flow_movie_collection_uses_file_sizes(self, renamer):
|
||||
"""多文件电影种子走完整 rename 流程时,按文件体积选出主文件,
|
||||
目标文件名互不相同。"""
|
||||
renamer.client.client.torrents_info.return_value = [
|
||||
{
|
||||
"hash": "h1",
|
||||
"name": "天气之子 Movie BDRip",
|
||||
"save_path": "/downloads/Bangumi/天气之子 (2019)",
|
||||
}
|
||||
]
|
||||
renamer.client.client.torrents_files.return_value = [
|
||||
{"name": "Menu PV.mkv", "size": 100_000_000},
|
||||
{"name": "Tenki no Ko.mkv", "size": 8_000_000_000},
|
||||
]
|
||||
renamer.client.client.torrents_rename_file.return_value = True
|
||||
|
||||
with patch.object(
|
||||
renamer,
|
||||
"_batch_lookup_offsets",
|
||||
AsyncMock(return_value={"h1": (0, 0, "movie")}),
|
||||
):
|
||||
with patch("module.manager.renamer.settings") as mock_settings:
|
||||
mock_settings.bangumi_manage.rename_method = "advance"
|
||||
mock_settings.bangumi_manage.remove_bad_torrent = False
|
||||
with patch("module.downloader.path.settings") as mock_path_settings:
|
||||
mock_path_settings.downloader.path = "/downloads/Bangumi"
|
||||
await renamer.rename()
|
||||
|
||||
calls = renamer.client.client.torrents_rename_file.call_args_list
|
||||
by_old = {c.kwargs["old_path"]: c.kwargs["new_path"] for c in calls}
|
||||
assert by_old["Tenki no Ko.mkv"] == "天气之子 (2019).mkv"
|
||||
assert by_old["Menu PV.mkv"] == "天气之子 (2019) - Menu PV.mkv"
|
||||
|
||||
async def test_no_media_files_no_crash(self, renamer):
|
||||
"""When torrent has no media files, logs warning but doesn't crash."""
|
||||
renamer.client.client.torrents_info.return_value = [
|
||||
|
||||
Reference in New Issue
Block a user