last clean code refactor
This commit is contained in:
parent
14c0573d1b
commit
395a5a8226
2
pom.xml
2
pom.xml
|
@ -42,7 +42,7 @@
|
||||||
<artifactId>resteasy-client</artifactId>
|
<artifactId>resteasy-client</artifactId>
|
||||||
<version>3.5.0.Final</version>
|
<version>3.5.0.Final</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- needed for java 17 compatibility -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>javax.xml</groupId>
|
<groupId>javax.xml</groupId>
|
||||||
<artifactId>jaxb-api</artifactId>
|
<artifactId>jaxb-api</artifactId>
|
||||||
|
|
|
@ -18,7 +18,6 @@ public class ConfigFileLoader extends CommonConfigFileLoader<ConfigFileLoader.Pr
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ProjectConfiguration {
|
public enum ProjectConfiguration {
|
||||||
UPLOADS_PATHS
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -9,71 +9,75 @@ import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class FailedImportFixer {
|
class FailedImportFixer {
|
||||||
public static final int MINIMUM_FILE_SIZE_TO_BE_CONSIDERED_A_VIDEO = 300000;
|
static final int MINIMUM_FILE_SIZE_TO_BE_CONSIDERED_A_VIDEO = 300000;
|
||||||
private final Record queueRecord;
|
|
||||||
private final MissingFilesDetector missingFilesDetector = new MissingFilesDetector();
|
|
||||||
private final Path torrentPath;
|
|
||||||
private final Path seriePath;
|
|
||||||
private final FileCopier fileCopier;
|
private final FileCopier fileCopier;
|
||||||
private Path seasonPath;
|
private final MissingFilesDetector missingFilesDetector;
|
||||||
|
|
||||||
|
private final Path torrentPath;
|
||||||
|
private final String torrentTitle;
|
||||||
|
private final Path seasonPath;
|
||||||
|
|
||||||
static Factory factory() {
|
static Factory factory() {
|
||||||
return new Factory();
|
return new Factory();
|
||||||
}
|
}
|
||||||
public static class Factory {
|
static class Factory {
|
||||||
FailedImportFixer newFixerFor(Record queueRecord, SonarrSerie serie){
|
FailedImportFixer newFixerFor(Record queueRecord, SonarrSerie serie){
|
||||||
return new FailedImportFixer(queueRecord, serie);
|
return new FailedImportFixer(queueRecord, serie);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private FailedImportFixer(Record queueRecord, SonarrSerie serie) {
|
private FailedImportFixer(Record queueRecord, SonarrSerie serie) {
|
||||||
this.queueRecord = queueRecord;
|
|
||||||
this.torrentPath = Path.of(queueRecord.getOutputPath());
|
|
||||||
this.seriePath = Path.of(serie.getPath());
|
|
||||||
fileCopier = new FileCopier();
|
fileCopier = new FileCopier();
|
||||||
|
missingFilesDetector = new MissingFilesDetector();
|
||||||
|
|
||||||
|
torrentPath = Path.of(queueRecord.getOutputPath());
|
||||||
|
torrentTitle = queueRecord.getTitle();
|
||||||
|
|
||||||
|
var seriePath = Path.of(serie.getPath());
|
||||||
|
seasonPath = seriePath.resolve(tryGettingSeasonFolder());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String tryGettingSeasonFolder() {
|
||||||
|
try {
|
||||||
|
return StringCaptor.getSeasonFolderNameFromSeason(torrentTitle);
|
||||||
|
} catch (IncorrectWorkingReferencesException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new SeasonFolderUnretrievable();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void fix() throws IOException {
|
void fix() throws IOException {
|
||||||
System.out.printf("%nfixing: %s%n" ,queueRecord.getTitle());
|
System.out.printf("%nfixing: %s%n" , torrentTitle);
|
||||||
List<Path> torrentFiles = resolveTorrentFiles();
|
List<Path> torrentFiles = getVideoFilesFrom(torrentPath);
|
||||||
List<Path> sonarFiles = resolveSonarrFiles();
|
List<Path> sonarFiles = getVideoFilesFrom(seasonPath);
|
||||||
List<Path> filesToCopy = missingFilesDetector.getMissingFilesAtDestination(torrentFiles, sonarFiles);
|
List<Path> filesToCopy = missingFilesDetector.getMissingFilesAtDestination(torrentFiles, sonarFiles);
|
||||||
filesToCopy.forEach(this::copy);
|
filesToCopy.forEach(this::copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Path> resolveTorrentFiles() throws IOException {
|
|
||||||
return getVideoFilesFrom(torrentPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Path> resolveSonarrFiles() throws IOException {
|
|
||||||
seasonPath = seriePath.resolve(getSeasonFolder());
|
|
||||||
return getVideoFilesFrom(seasonPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getSeasonFolder() {
|
|
||||||
try {
|
|
||||||
return StringCaptor.getSeasonFolderNameFromSeason(torrentPath.getFileName().toString());
|
|
||||||
} catch (IncorrectWorkingReferencesException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Path> getVideoFilesFrom(Path path) throws IOException {
|
private List<Path> getVideoFilesFrom(Path path) throws IOException {
|
||||||
System.out.println("going to explore "+path);
|
System.out.println("going to explore "+path);
|
||||||
try (var pathWalk = Files.walk(path, 3)) {
|
try (var pathsWalk = Files.walk(path, 3)) {
|
||||||
return pathWalk
|
return pathsWalk
|
||||||
.filter(p -> p.toFile().isFile())
|
.filter(this::isFile)
|
||||||
.filter(p -> p.toFile().length() > MINIMUM_FILE_SIZE_TO_BE_CONSIDERED_A_VIDEO)
|
.filter(this::isAVideo)
|
||||||
.collect(Collectors.toList());
|
.toList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private boolean isFile(Path path) {
|
||||||
|
return path.toFile().isFile();
|
||||||
|
}
|
||||||
|
private boolean isAVideo(Path path) {
|
||||||
|
return path.toFile().length() > MINIMUM_FILE_SIZE_TO_BE_CONSIDERED_A_VIDEO;
|
||||||
|
}
|
||||||
|
|
||||||
private void copy(Path fileToCopy) {
|
private void copy(Path fileToCopy) {
|
||||||
Path target = seasonPath.resolve(fileToCopy.getFileName());
|
Path target = seasonPath.resolve(fileToCopy.getFileName());
|
||||||
System.out.println("** going to hardlink file to "+target);
|
System.out.println("** going to hardlink file to "+target);
|
||||||
fileCopier.hardLink(fileToCopy, target);
|
fileCopier.hardLink(fileToCopy, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class SeasonFolderUnretrievable extends RuntimeException {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
public class FileCopier {
|
class FileCopier {
|
||||||
void hardLink(Path source, Path destination) {
|
void hardLink(Path source, Path destination) {
|
||||||
try {
|
try {
|
||||||
Files.createLink(destination, source);
|
Files.createLink(destination, source);
|
||||||
|
@ -15,10 +15,3 @@ public class FileCopier {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProjectPath {
|
|
||||||
static Path of(String path) {
|
|
||||||
String projectPath = System.getProperty("user.dir");
|
|
||||||
return Path.of(projectPath + path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ public class MainWorker {
|
||||||
worker.work();
|
worker.work();
|
||||||
}
|
}
|
||||||
|
|
||||||
public MainWorker() throws IncorrectWorkingReferencesException {
|
private MainWorker() throws IncorrectWorkingReferencesException {
|
||||||
var configLoader = ConfigFileLoader.getLoader();
|
var configLoader = ConfigFileLoader.getLoader();
|
||||||
Sonarr.initService(configLoader);
|
Sonarr.initService(configLoader);
|
||||||
queueFixer = new QueueFixer();
|
queueFixer = new QueueFixer();
|
||||||
|
|
|
@ -5,10 +5,10 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class MissingFilesDetector {
|
class MissingFilesDetector {
|
||||||
|
|
||||||
private Map<Long, List<Path>> sonarrFilesByLength;
|
private Map<Long, List<Path>> sonarrFilesByLength;
|
||||||
private Map<Long, List<Path>> torrentFileLengths;
|
private Map<Long, List<Path>> torrentFilesByLength;
|
||||||
|
|
||||||
List<Path> getMissingFilesAtDestination(List<Path> torrentFiles, List<Path> sonarrFiles) {
|
List<Path> getMissingFilesAtDestination(List<Path> torrentFiles, List<Path> sonarrFiles) {
|
||||||
System.out.printf("going to compare %d torrent files with %d sonar files%n",
|
System.out.printf("going to compare %d torrent files with %d sonar files%n",
|
||||||
|
@ -19,8 +19,8 @@ public class MissingFilesDetector {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void digestTorrentFiles(List<Path> torrentFiles) {
|
private void digestTorrentFiles(List<Path> torrentFiles) {
|
||||||
torrentFileLengths = getFileLengthsMapFrom(torrentFiles);
|
torrentFilesByLength = getFileLengthsMapFrom(torrentFiles);
|
||||||
throwIfDuplicatedSizes(torrentFileLengths);
|
throwIfDuplicatedSizes(torrentFilesByLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void digestSonarrFiles(List<Path> sonarrFiles) {
|
private void digestSonarrFiles(List<Path> sonarrFiles) {
|
||||||
|
@ -28,35 +28,8 @@ public class MissingFilesDetector {
|
||||||
throwIfDuplicatedSizes(sonarrFilesByLength);
|
throwIfDuplicatedSizes(sonarrFilesByLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<Long, List<Path>> getFileLengthsMapFrom(List<Path> files) {
|
|
||||||
return files.stream()
|
|
||||||
.collect(Collectors.groupingBy(p -> p.toFile().length()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void throwIfDuplicatedSizes(Map<Long, List<Path>> torrentFileLengths) {
|
|
||||||
if (hasFilesWithSameSize(torrentFileLengths))
|
|
||||||
throw new InsecureScenario();
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean hasFilesWithSameSize(Map<Long, List<Path>> torrentFileLengths) {
|
|
||||||
return torrentFileLengths
|
|
||||||
.values()
|
|
||||||
.stream()
|
|
||||||
.anyMatch(this::hasMultipleElements);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean hasMultipleElements(List<Path> paths) {
|
|
||||||
if (paths.size() > 1) {
|
|
||||||
var sampleFile = paths.get(0).toFile();
|
|
||||||
System.out.printf("!!! There is more than one file with the same size of %d. Name: %s %n",
|
|
||||||
sampleFile.length(), sampleFile.getName());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Path> resolveMissingFiles() {
|
private List<Path> resolveMissingFiles() {
|
||||||
return torrentFileLengths.entrySet()
|
return torrentFilesByLength.entrySet()
|
||||||
.stream()
|
.stream()
|
||||||
.filter(this::missingAtDestination)
|
.filter(this::missingAtDestination)
|
||||||
.map(Map.Entry::getValue)
|
.map(Map.Entry::getValue)
|
||||||
|
@ -64,6 +37,33 @@ public class MissingFilesDetector {
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<Long, List<Path>> getFileLengthsMapFrom(List<Path> files) {
|
||||||
|
return files.stream()
|
||||||
|
.collect(Collectors.groupingBy(p -> p.toFile().length()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void throwIfDuplicatedSizes(Map<Long, List<Path>> filesByLength) {
|
||||||
|
if (hasFilesWithSameSize(filesByLength))
|
||||||
|
throw new InsecureScenario();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasFilesWithSameSize(Map<Long, List<Path>> filesByLength) {
|
||||||
|
return filesByLength
|
||||||
|
.values()
|
||||||
|
.stream()
|
||||||
|
.anyMatch(this::hasMultipleElements);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasMultipleElements(List<Path> elements) {
|
||||||
|
if (elements.size() > 1) {
|
||||||
|
var sampleFile = elements.get(0).toFile();
|
||||||
|
System.out.printf("!!! There is more than one file with the same size of %d. Name: %s %n",
|
||||||
|
sampleFile.length(), sampleFile.getName());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean missingAtDestination(Map.Entry<Long, List<Path>> torrentFileEntry) {
|
private boolean missingAtDestination(Map.Entry<Long, List<Path>> torrentFileEntry) {
|
||||||
return !sonarrFilesByLength.containsKey(torrentFileEntry.getKey());
|
return !sonarrFilesByLength.containsKey(torrentFileEntry.getKey());
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,22 +9,22 @@ import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class QueueFixer {
|
public class QueueFixer {
|
||||||
final static String IMPORT_FAILURE_BECAUSE_MATCHED_BY_ID = "Found matching series via grab history, but release was matched to series by ID. Automatic import is not possible. See the FAQ for details.";
|
final static String IMPORT_FAILURE_BECAUSE_MATCHED_BY_ID = "Found matching series via grab history, but release was matched to series by ID. Automatic import is not possible. See the FAQ for details.";
|
||||||
|
|
||||||
private final SonarrApiGateway sonarrApiGateway;
|
private final SonarrApiGateway sonarrApiGateway;
|
||||||
private final FailedImportFixer.Factory failedImportFixerFactory;
|
private final FailedImportFixer.Factory fixerFactory;
|
||||||
|
|
||||||
QueueFixer() {
|
QueueFixer() {
|
||||||
sonarrApiGateway = Sonarr.api();
|
sonarrApiGateway = Sonarr.api();
|
||||||
failedImportFixerFactory = FailedImportFixer.factory();
|
fixerFactory = FailedImportFixer.factory();
|
||||||
}
|
}
|
||||||
|
|
||||||
void fix() {
|
void fix() {
|
||||||
List<Record> sonarQueue = retrieveQueueRecordsFromSonarr();
|
List<Record> sonarQueue = retrieveQueueRecordsFromSonarr();
|
||||||
Collection<Record> records = deduplicate(sonarQueue);
|
var distinctRecords = deduplicate(sonarQueue);
|
||||||
List<Record> recordsToFix = filterFailedImportsOfIdProblem(records);
|
var recordsToFix = filterFailedImportsOfIdProblem(distinctRecords);
|
||||||
recordsToFix.forEach(this::try2FixFailedImport);
|
recordsToFix.forEach(this::try2FixFailedImport);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,9 +33,9 @@ public class QueueFixer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Collection<Record> deduplicate(List<Record> repetitiveRecords) {
|
private Collection<Record> deduplicate(List<Record> repetitiveRecords) {
|
||||||
Map<String, Record> recordsByTitle = new HashMap<>();
|
var recordsByTitle = new HashMap<String, Record>();
|
||||||
repetitiveRecords.forEach(record ->
|
for (var record : repetitiveRecords)
|
||||||
recordsByTitle.putIfAbsent(record.getTitle(), record));
|
recordsByTitle.putIfAbsent(record.getTitle(), record);
|
||||||
return recordsByTitle.values();
|
return recordsByTitle.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,19 +45,13 @@ public class QueueFixer {
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean recordsWithImportFailureBecauseIdMatching(Record record) {
|
|
||||||
return record.getStatusMessages().stream()
|
|
||||||
.flatMap(status -> status.getMessages().stream())
|
|
||||||
.anyMatch(IMPORT_FAILURE_BECAUSE_MATCHED_BY_ID::equals);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void try2FixFailedImport(Record record) {
|
private void try2FixFailedImport(Record record) {
|
||||||
try {
|
try {
|
||||||
var seriesId = record.getSeriesId();
|
var seriesId = record.getSeriesId();
|
||||||
SonarrSerie serie = getSerieFromSonarr(seriesId);
|
SonarrSerie serie = sonarrApiGateway.getSerieById(seriesId);
|
||||||
if (serie == null) return;
|
if (serie == null) return;
|
||||||
|
|
||||||
failedImportFixerFactory
|
fixerFactory
|
||||||
.newFixerFor(record, serie)
|
.newFixerFor(record, serie)
|
||||||
.fix();
|
.fix();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -66,8 +60,10 @@ public class QueueFixer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private SonarrSerie getSerieFromSonarr(Integer seriesId) {
|
private boolean recordsWithImportFailureBecauseIdMatching(Record record) {
|
||||||
return sonarrApiGateway.getSerieById(seriesId);
|
return record.getStatusMessages().stream()
|
||||||
|
.flatMap(status -> status.getMessages().stream())
|
||||||
|
.anyMatch(IMPORT_FAILURE_BECAUSE_MATCHED_BY_ID::equals);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue