Skip to content

Commit 69414cd

Browse files
committed
cleanup
1 parent a962708 commit 69414cd

21 files changed

Lines changed: 587 additions & 80 deletions

daemon/postprocess/Cleanup.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,14 +120,14 @@ bool MoveController::MoveFiles(const fs::path& src, const fs::path& dest)
120120
auto filename = fs::u8string(it->path().filename());
121121
if (filename[0] == '.')
122122
{
123-
if (it->is_directory())
123+
if (it->is_directory(ec))
124124
{
125125
it.disable_recursion_pending();
126126
}
127127
continue;
128128
}
129129

130-
if (it->is_directory())
130+
if (it->is_directory(ec))
131131
{
132132
dirs.push_back(it->path());
133133
}
@@ -137,6 +137,11 @@ bool MoveController::MoveFiles(const fs::path& src, const fs::path& dest)
137137
}
138138
}
139139

140+
if (IsStopped() || ec)
141+
{
142+
return false;
143+
}
144+
140145
for (const auto& d : dirs)
141146
{
142147
if (IsStopped()) return false;
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* This file is part of nzbget. See <https://nzbget.com>.
3+
*
4+
* Copyright (C) 2026 Denis <denis@nzbget.com>
5+
*
6+
* This program is free software; you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation; either version 2 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
18+
*/
19+
20+
21+
#include "nzbget.h"
22+
#include "CollectionAnalyzer.h"
23+
#include "FileTypes.h"
24+
25+
namespace PostRenamer
26+
{
27+
28+
std::vector<CollectionAnalyzer::FileGroup>
29+
CollectionAnalyzer::BuildGroups(const std::vector<Candidate>& candidates)
30+
{
31+
std::vector<FileGroup> groups;
32+
for (const Candidate& candidate : candidates)
33+
{
34+
if (FileTypes::IsSubtitleExt(candidate.ext) || FileTypes::IsSampleStem(candidate.stem)) continue;
35+
36+
FileGroup* targetGroup = nullptr;
37+
for (FileGroup& group : groups)
38+
{
39+
if (group.parentDir == candidate.parentDir && group.extKey == candidate.extLower)
40+
{
41+
targetGroup = &group;
42+
break;
43+
}
44+
}
45+
46+
if (!targetGroup)
47+
{
48+
groups.push_back({candidate.parentDir, candidate.extLower, 0, 0, 0, false});
49+
targetGroup = &groups.back();
50+
}
51+
52+
targetGroup->count++;
53+
uintmax_t size = candidate.size;
54+
if (size > targetGroup->largest)
55+
{
56+
targetGroup->second = targetGroup->largest;
57+
targetGroup->largest = size;
58+
}
59+
else if (size > targetGroup->second)
60+
{
61+
targetGroup->second = size;
62+
}
63+
}
64+
65+
for (FileGroup& group : groups)
66+
{
67+
bool isAmbiguousCollection = (group.count >= 2 && group.largest <= group.second * 3);
68+
bool isAudio = FileTypes::IsAudioExt(group.extKey);
69+
group.skip = isAmbiguousCollection && !isAudio;
70+
}
71+
72+
return groups;
73+
}
74+
75+
CollectionAnalyzer::CollectionAnalyzer(const std::vector<Candidate>& candidates)
76+
: m_groups(BuildGroups(candidates))
77+
{
78+
}
79+
80+
bool CollectionAnalyzer::ShouldSkip(const Candidate& candidate) const
81+
{
82+
bool isSub = FileTypes::IsSubtitleExt(candidate.ext);
83+
bool isSample = FileTypes::IsSampleStem(candidate.stem);
84+
85+
if (isSub || isSample)
86+
{
87+
bool hasNonAudioGroup = false;
88+
bool allNonAudioSkipped = true;
89+
90+
for (const FileGroup& group : m_groups)
91+
{
92+
if (group.parentDir == candidate.parentDir && !FileTypes::IsAudioExt(group.extKey))
93+
{
94+
hasNonAudioGroup = true;
95+
if (!group.skip)
96+
{
97+
allNonAudioSkipped = false;
98+
}
99+
}
100+
}
101+
102+
return hasNonAudioGroup && allNonAudioSkipped;
103+
}
104+
105+
for (const FileGroup& group : m_groups)
106+
{
107+
if (group.parentDir == candidate.parentDir && group.extKey == candidate.extLower)
108+
{
109+
return group.skip;
110+
}
111+
}
112+
113+
return false;
114+
}
115+
116+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* This file is part of nzbget. See <https://nzbget.com>.
3+
*
4+
* Copyright (C) 2026 Denis <denis@nzbget.com>
5+
*
6+
* This program is free software; you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation; either version 2 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
18+
*/
19+
20+
21+
#ifndef COLLECTION_ANALYZER_H
22+
#define COLLECTION_ANALYZER_H
23+
24+
#include <string>
25+
#include <vector>
26+
#include <cstdint>
27+
#include "PostRenamer.h"
28+
29+
namespace PostRenamer
30+
{
31+
32+
class CollectionAnalyzer final
33+
{
34+
public:
35+
explicit CollectionAnalyzer(const std::vector<Candidate>& candidates);
36+
bool ShouldSkip(const Candidate& candidate) const;
37+
38+
private:
39+
struct FileGroup
40+
{
41+
fs::path parentDir;
42+
std::string extKey;
43+
uintmax_t largest = 0;
44+
uintmax_t second = 0;
45+
int count = 0;
46+
bool skip = false;
47+
};
48+
49+
std::vector<FileGroup> m_groups;
50+
51+
static std::vector<FileGroup> BuildGroups(const std::vector<Candidate>& candidates);
52+
};
53+
54+
}
55+
56+
#endif
Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,16 @@
1919

2020

2121
#include "nzbget.h"
22-
#include "PostUnpackRenamer.h"
22+
#include "PostRenamer.h"
23+
#include "CollectionAnalyzer.h"
2324
#include "Options.h"
2425
#include "Log.h"
2526
#include "FileSystem.h"
2627
#include "Deobfuscation.h"
2728
#include "FileTypes.h"
29+
#include "Util.h"
2830

29-
namespace PostUnpackRenamer
31+
namespace PostRenamer
3032
{
3133

3234
void Controller::StartJob(PostInfo* postInfo)
@@ -35,7 +37,7 @@ void Controller::StartJob(PostInfo* postInfo)
3537

3638
if (!renamer)
3739
{
38-
error("Failed to allocate memory for PostUnpackRenamer::Controller");
40+
error("Failed to allocate memory for PostRenamer::Controller");
3941
return;
4042
}
4143

@@ -133,9 +135,9 @@ std::string Controller::ResolveUniqueName(std::string_view metaname, std::string
133135
return candidate;
134136
}
135137

136-
std::vector<fs::path> Controller::CollectCandidates(const fs::path& dir)
138+
std::vector<PostRenamer::Candidate> Controller::CollectCandidates(const fs::path& dir)
137139
{
138-
std::vector<fs::path> candidates;
140+
std::vector<Candidate> candidates;
139141

140142
fs::error_code ec;
141143
for (auto it = fs::recursive_directory_iterator(dir, fs::directory_options::skip_permission_denied, ec);
@@ -152,7 +154,18 @@ std::vector<fs::path> Controller::CollectCandidates(const fs::path& dir)
152154

153155
if (Util::MatchFileExt(filename.c_str(), g_Options->GetRenameIgnoreExt(), ",")) continue;
154156

155-
candidates.push_back(it->path());
157+
Candidate candidate;
158+
candidate.path = it->path();
159+
candidate.parentDir = candidate.path.parent_path();
160+
candidate.filename = filename;
161+
candidate.stem = fs::u8string(candidate.path.stem());
162+
candidate.ext = fs::u8string(candidate.path.extension());
163+
candidate.extLower = candidate.ext;
164+
std::transform(candidate.extLower.begin(), candidate.extLower.end(), candidate.extLower.begin(),
165+
[](unsigned char c) { return std::tolower(c); });
166+
candidate.size = fs::file_size(candidate.path, ec);
167+
if (ec) candidate.size = 0;
168+
candidates.push_back(std::move(candidate));
156169
}
157170
return candidates;
158171
}
@@ -181,17 +194,19 @@ int Controller::RenameFiles(PostInfo* postInfo)
181194
}
182195

183196
auto candidates = CollectCandidates(destPath);
197+
CollectionAnalyzer analyzer(candidates);
184198

185199
std::set<fs::path> usedPaths;
186200
int renamedCount = 0;
187201

188-
for (const fs::path& fullPath : candidates)
202+
for (Candidate& candidate : candidates)
189203
{
190204
if (IsStopped()) break;
205+
if (analyzer.ShouldSkip(candidate)) continue;
191206

192-
std::string ext = fs::u8string(fullPath.extension());
193-
std::string stem = fs::u8string(fullPath.stem());
194-
std::string filename = fs::u8string(fullPath.filename());
207+
const std::string& filename = candidate.filename;
208+
const std::string& stem = candidate.stem;
209+
const std::string& ext = candidate.ext;
195210
std::string newName;
196211

197212
if (FileTypes::IsSubtitleExt(ext))
@@ -207,23 +222,23 @@ int Controller::RenameFiles(PostInfo* postInfo)
207222
newName = metaname + ext;
208223
}
209224

210-
fs::path parentDir = fullPath.parent_path();
211-
std::string candidate = ResolveUniqueName(metaname, stem, ext, newName, usedPaths, parentDir);
212-
fs::path newPath = parentDir / candidate;
225+
fs::path parentDir = candidate.path.parent_path();
226+
std::string candidateName = ResolveUniqueName(metaname, stem, ext, newName, usedPaths, parentDir);
227+
fs::path newPath = parentDir / candidateName;
213228
usedPaths.insert(newPath);
214229

215-
nzbInfo->PrintMessage(Message::mkInfo, "Renaming obfuscated file %s to %s", filename.c_str(), candidate.c_str());
230+
nzbInfo->PrintMessage(Message::mkInfo, "Renaming obfuscated file %s to %s", filename.c_str(), candidateName.c_str());
216231
fs::error_code moveEc;
217-
fs::move_file(fullPath, newPath, moveEc);
232+
fs::move_file(candidate.path, newPath, moveEc);
218233
if (moveEc)
219234
{
220235
nzbInfo->PrintMessage(Message::mkWarning,
221236
"Could not rename obfuscated file %s to %s: %s",
222-
filename.c_str(), candidate.c_str(), moveEc.message().c_str());
237+
filename.c_str(), candidateName.c_str(), moveEc.message().c_str());
223238
continue;
224239
}
225240

226-
if (!nzbInfo->RenameCompletedFile(filename.c_str(), candidate.c_str()))
241+
if (!nzbInfo->RenameCompletedFile(filename.c_str(), candidateName.c_str()))
227242
{
228243
nzbInfo->PrintMessage(Message::mkWarning,
229244
"Could not update completed file record for %s", filename.c_str());
Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
*/
1919

2020

21-
#ifndef POST_UNPACK_RENAMER_H
22-
#define POST_UNPACK_RENAMER_H
21+
#ifndef POST_RENAMER_H
22+
#define POST_RENAMER_H
2323

2424
#include "Thread.h"
2525
#include "DownloadInfo.h"
@@ -28,9 +28,20 @@
2828
#include <string>
2929
#include <string_view>
3030

31-
namespace PostUnpackRenamer
31+
namespace PostRenamer
3232
{
3333

34+
struct Candidate
35+
{
36+
fs::path path;
37+
fs::path parentDir;
38+
std::string filename;
39+
std::string stem;
40+
std::string ext;
41+
std::string extLower;
42+
uintmax_t size = 0;
43+
};
44+
3445
class Controller final : public Thread
3546
{
3647
public:
@@ -47,7 +58,7 @@ class Controller final : public Thread
4758
PostInfo* m_postInfo = nullptr;
4859
int m_renamedCount = 0;
4960

50-
std::vector<fs::path> CollectCandidates(const fs::path& dir);
61+
std::vector<Candidate> CollectCandidates(const fs::path& dir);
5162
void RenameCompleted();
5263
};
5364

0 commit comments

Comments
 (0)