1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-04-27 22:57:09 -04:00

New: Import subtitles from sub folders

This commit is contained in:
Stéphane Dupont
2022-02-27 21:37:23 +01:00
committed by Qstick
parent c8ab4f8c68
commit dd5bc41eda
10 changed files with 767 additions and 68 deletions
+20 -41
View File
@@ -31,7 +31,6 @@ namespace NzbDrone.Core.Extras
private readonly IDiskProvider _diskProvider;
private readonly IConfigService _configService;
private readonly List<IManageExtraFiles> _extraFileManagers;
private readonly Logger _logger;
public ExtraService(IMediaFileService mediaFileService,
IMovieService movieService,
@@ -45,7 +44,6 @@ namespace NzbDrone.Core.Extras
_diskProvider = diskProvider;
_configService = configService;
_extraFileManagers = extraFileManagers.OrderBy(e => e.Order).ToList();
_logger = logger;
}
public void ImportMovie(LocalMovie localMovie, MovieFile movieFile, bool isReadOnly)
@@ -62,61 +60,42 @@ namespace NzbDrone.Core.Extras
return;
}
var sourcePath = localMovie.Path;
var sourceFolder = _diskProvider.GetParentFolder(sourcePath);
var sourceFileName = Path.GetFileNameWithoutExtension(sourcePath);
var files = _diskProvider.GetFiles(sourceFolder, SearchOption.AllDirectories).Where(f => f != localMovie.Path);
var folderSearchOption = localMovie.FolderMovieInfo == null
? SearchOption.TopDirectoryOnly
: SearchOption.AllDirectories;
var wantedExtensions = _configService.ExtraFileExtensions.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(e => e.Trim(' ', '.'))
.Select(e => e.Trim(' ', '.')
.Insert(0, "."))
.ToList();
var matchingFilenames = files.Where(f => Path.GetFileNameWithoutExtension(f).StartsWith(sourceFileName, StringComparison.InvariantCultureIgnoreCase)).ToList();
var filteredFilenames = new List<string>();
var hasNfo = false;
var sourceFolder = _diskProvider.GetParentFolder(localMovie.Path);
var files = _diskProvider.GetFiles(sourceFolder, folderSearchOption);
var managedFiles = _extraFileManagers.Select((i) => new List<string>()).ToArray();
foreach (var matchingFilename in matchingFilenames)
foreach (var file in files)
{
// Filter out duplicate NFO files
if (matchingFilename.EndsWith(".nfo", StringComparison.InvariantCultureIgnoreCase))
{
if (hasNfo)
{
continue;
}
hasNfo = true;
}
filteredFilenames.Add(matchingFilename);
}
foreach (var matchingFilename in filteredFilenames)
{
var matchingExtension = wantedExtensions.FirstOrDefault(e => matchingFilename.EndsWith(e));
var extension = Path.GetExtension(file);
var matchingExtension = wantedExtensions.FirstOrDefault(e => e.Equals(extension));
if (matchingExtension == null)
{
continue;
}
try
for (int i = 0; i < _extraFileManagers.Count; i++)
{
foreach (var extraFileManager in _extraFileManagers)
if (_extraFileManagers[i].CanImportFile(localMovie, movieFile, file, extension, isReadOnly))
{
var extension = Path.GetExtension(matchingFilename);
var extraFile = extraFileManager.Import(localMovie.Movie, movieFile, matchingFilename, extension, isReadOnly);
if (extraFile != null)
{
break;
}
managedFiles[i].Add(file);
break;
}
}
catch (Exception ex)
{
_logger.Warn(ex, "Failed to import extra file: {0}", matchingFilename);
}
}
for (int i = 0; i < _extraFileManagers.Count; i++)
{
_extraFileManagers[i].ImportFiles(localMovie, movieFile, managedFiles[i], isReadOnly);
}
}
@@ -8,6 +8,7 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Extras.Files
{
@@ -19,7 +20,8 @@ namespace NzbDrone.Core.Extras.Files
IEnumerable<ExtraFile> CreateAfterMovieImport(Movie movie, MovieFile movieFile);
IEnumerable<ExtraFile> CreateAfterMovieFolder(Movie movie, string movieFolder);
IEnumerable<ExtraFile> MoveFilesAfterRename(Movie movie, List<MovieFile> movieFiles);
ExtraFile Import(Movie movie, MovieFile movieFile, string path, string extension, bool readOnly);
bool CanImportFile(LocalMovie localMovie, MovieFile movieFile, string path, string extension, bool readOnly);
IEnumerable<ExtraFile> ImportFiles(LocalMovie localMovie, MovieFile movieFile, List<string> files, bool isReadOnly);
}
public abstract class ExtraFileManager<TExtraFile> : IManageExtraFiles
@@ -47,7 +49,8 @@ namespace NzbDrone.Core.Extras.Files
public abstract IEnumerable<ExtraFile> CreateAfterMovieImport(Movie movie, MovieFile movieFile);
public abstract IEnumerable<ExtraFile> CreateAfterMovieFolder(Movie movie, string movieFolder);
public abstract IEnumerable<ExtraFile> MoveFilesAfterRename(Movie movie, List<MovieFile> movieFiles);
public abstract ExtraFile Import(Movie movie, MovieFile movieFile, string path, string extension, bool readOnly);
public abstract bool CanImportFile(LocalMovie localMovie, MovieFile movieFile, string path, string extension, bool readOnly);
public abstract IEnumerable<ExtraFile> ImportFiles(LocalMovie localMovie, MovieFile movieFile, List<string> files, bool isReadOnly);
protected TExtraFile ImportFile(Movie movie, MovieFile movieFile, string path, bool readOnly, string extension, string fileNameSuffix = null)
{
@@ -13,6 +13,7 @@ using NzbDrone.Core.Extras.Metadata.Files;
using NzbDrone.Core.Extras.Others;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Extras.Metadata
{
@@ -191,9 +192,14 @@ namespace NzbDrone.Core.Extras.Metadata
return movedFiles;
}
public override ExtraFile Import(Movie movie, MovieFile movieFile, string path, string extension, bool readOnly)
public override bool CanImportFile(LocalMovie localMovie, MovieFile movieFile, string path, string extension, bool readOnly)
{
return null;
return false;
}
public override IEnumerable<ExtraFile> ImportFiles(LocalMovie localMovie, MovieFile movieFile, List<string> files, bool isReadOnly)
{
return Enumerable.Empty<ExtraFile>();
}
private List<MetadataFile> GetMetadataFilesForConsumer(IMetadata consumer, List<MetadataFile> movieMetadata)
@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NLog;
using NzbDrone.Common.Disk;
@@ -7,13 +9,16 @@ using NzbDrone.Core.Configuration;
using NzbDrone.Core.Extras.Files;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Extras.Others
{
public class OtherExtraService : ExtraFileManager<OtherExtraFile>
{
private readonly IDiskProvider _diskProvider;
private readonly IOtherExtraFileService _otherExtraFileService;
private readonly IMediaFileAttributeService _mediaFileAttributeService;
private readonly Logger _logger;
public OtherExtraService(IConfigService configService,
IDiskProvider diskProvider,
@@ -23,8 +28,10 @@ namespace NzbDrone.Core.Extras.Others
Logger logger)
: base(configService, diskProvider, diskTransferService, logger)
{
_diskProvider = diskProvider;
_otherExtraFileService = otherExtraFileService;
_mediaFileAttributeService = mediaFileAttributeService;
_logger = logger;
}
public override int Order => 2;
@@ -69,17 +76,79 @@ namespace NzbDrone.Core.Extras.Others
return movedFiles;
}
public override ExtraFile Import(Movie movie, MovieFile movieFile, string path, string extension, bool readOnly)
public override bool CanImportFile(LocalMovie localMovie, MovieFile movieFile, string path, string extension, bool readOnly)
{
var extraFile = ImportFile(movie, movieFile, path, readOnly, extension, null);
return true;
}
if (extraFile != null)
public override IEnumerable<ExtraFile> ImportFiles(LocalMovie localMovie, MovieFile movieFile, List<string> files, bool isReadOnly)
{
var importedFiles = new List<ExtraFile>();
var filteredFiles = files.Where(f => CanImportFile(localMovie, movieFile, f, Path.GetExtension(f), isReadOnly)).ToList();
var sourcePath = localMovie.Path;
var sourceFolder = _diskProvider.GetParentFolder(sourcePath);
var sourceFileName = Path.GetFileNameWithoutExtension(sourcePath);
var matchingFiles = new List<string>();
var hasNfo = false;
foreach (var file in filteredFiles)
{
_mediaFileAttributeService.SetFilePermissions(path);
_otherExtraFileService.Upsert(extraFile);
try
{
// Filter out duplicate NFO files
if (file.EndsWith(".nfo", StringComparison.InvariantCultureIgnoreCase))
{
if (hasNfo)
{
continue;
}
hasNfo = true;
}
// Filename match
if (Path.GetFileNameWithoutExtension(file).StartsWith(sourceFileName, StringComparison.InvariantCultureIgnoreCase))
{
matchingFiles.Add(file);
continue;
}
// Movie match
var fileMovieInfo = Parser.Parser.ParseMoviePath(file) ?? new ParsedMovieInfo();
if (fileMovieInfo.MovieTitle == null)
{
continue;
}
if (fileMovieInfo.MovieTitle == localMovie.FileMovieInfo.MovieTitle &&
fileMovieInfo.Year.Equals(localMovie.FileMovieInfo.Year))
{
matchingFiles.Add(file);
}
}
catch (Exception ex)
{
_logger.Warn(ex, "Failed to import extra file: {0}", file);
}
}
return extraFile;
foreach (string file in matchingFiles)
{
try
{
var extraFile = ImportFile(localMovie.Movie, movieFile, file, isReadOnly, Path.GetExtension(file), null);
_mediaFileAttributeService.SetFilePermissions(file);
_otherExtraFileService.Upsert(extraFile);
importedFiles.Add(extraFile);
}
catch (Exception ex)
{
_logger.Warn(ex, "Failed to import extra file: {0}", file);
}
}
return importedFiles;
}
}
}
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -9,13 +10,17 @@ using NzbDrone.Core.Configuration;
using NzbDrone.Core.Extras.Files;
using NzbDrone.Core.Languages;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.MovieImport;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Extras.Subtitles
{
public class SubtitleService : ExtraFileManager<SubtitleFile>
{
private readonly IDiskProvider _diskProvider;
private readonly IDetectSample _detectSample;
private readonly ISubtitleFileService _subtitleFileService;
private readonly IMediaFileAttributeService _mediaFileAttributeService;
private readonly Logger _logger;
@@ -23,11 +28,14 @@ namespace NzbDrone.Core.Extras.Subtitles
public SubtitleService(IConfigService configService,
IDiskProvider diskProvider,
IDiskTransferService diskTransferService,
IDetectSample detectSample,
ISubtitleFileService subtitleFileService,
IMediaFileAttributeService mediaFileAttributeService,
Logger logger)
: base(configService, diskProvider, diskTransferService, logger)
{
_diskProvider = diskProvider;
_detectSample = detectSample;
_subtitleFileService = subtitleFileService;
_mediaFileAttributeService = mediaFileAttributeService;
_logger = logger;
@@ -71,11 +79,6 @@ namespace NzbDrone.Core.Extras.Subtitles
var groupCount = group.Count();
var copy = 1;
if (groupCount > 1)
{
_logger.Warn("Multiple subtitle files found with the same language and extension for {0}", Path.Combine(movie.Path, movieFile.RelativePath));
}
foreach (var subtitleFile in group)
{
var suffix = GetSuffix(subtitleFile.Language, copy, groupCount > 1);
@@ -91,22 +94,129 @@ namespace NzbDrone.Core.Extras.Subtitles
return movedFiles;
}
public override ExtraFile Import(Movie movie, MovieFile movieFile, string path, string extension, bool readOnly)
public override bool CanImportFile(LocalMovie localEpisode, MovieFile movieFile, string path, string extension, bool readOnly)
{
if (SubtitleFileExtensions.Extensions.Contains(Path.GetExtension(path)))
return SubtitleFileExtensions.Extensions.Contains(extension.ToLowerInvariant());
}
public override IEnumerable<ExtraFile> ImportFiles(LocalMovie localMovie, MovieFile movieFile, List<string> files, bool isReadOnly)
{
var importedFiles = new List<SubtitleFile>();
var filteredFiles = files.Where(f => CanImportFile(localMovie, movieFile, f, Path.GetExtension(f), isReadOnly)).ToList();
var sourcePath = localMovie.Path;
var sourceFolder = _diskProvider.GetParentFolder(sourcePath);
var sourceFileName = Path.GetFileNameWithoutExtension(sourcePath);
var matchingFiles = new List<string>();
foreach (var file in filteredFiles)
{
var language = LanguageParser.ParseSubtitleLanguage(path);
var suffix = GetSuffix(language, 1, false);
var subtitleFile = ImportFile(movie, movieFile, path, readOnly, extension, suffix);
subtitleFile.Language = language;
try
{
// Filename match
if (Path.GetFileNameWithoutExtension(file).StartsWith(sourceFileName, StringComparison.InvariantCultureIgnoreCase))
{
matchingFiles.Add(file);
continue;
}
_mediaFileAttributeService.SetFilePermissions(path);
_subtitleFileService.Upsert(subtitleFile);
// Movie match
var fileMovieInfo = Parser.Parser.ParseMoviePath(file) ?? new ParsedMovieInfo();
return subtitleFile;
if (fileMovieInfo.MovieTitle == null)
{
continue;
}
if (fileMovieInfo.MovieTitle == localMovie.FileMovieInfo.MovieTitle &&
fileMovieInfo.Year.Equals(localMovie.FileMovieInfo.Year))
{
matchingFiles.Add(file);
}
}
catch (Exception ex)
{
_logger.Warn(ex, "Failed to import subtitle file: {0}", file);
}
}
return null;
// Use any sub if only episode in folder
if (matchingFiles.Count == 0 && filteredFiles.Count > 0)
{
var videoFiles = _diskProvider.GetFiles(sourceFolder, SearchOption.AllDirectories)
.Where(file => MediaFileExtensions.Extensions.Contains(Path.GetExtension(file)))
.ToList();
if (videoFiles.Count() > 2)
{
return importedFiles;
}
// Filter out samples
videoFiles = videoFiles.Where(file =>
{
var sample = _detectSample.IsSample(localMovie.Movie.MovieMetadata, file);
if (sample == DetectSampleResult.Sample)
{
return false;
}
return true;
}).ToList();
if (videoFiles.Count == 1)
{
matchingFiles.AddRange(filteredFiles);
_logger.Warn("Imported any available subtitle file for movie: {0}", localMovie);
}
}
var subtitleFiles = new List<Tuple<string, Language, string>>();
foreach (string file in matchingFiles)
{
var language = LanguageParser.ParseSubtitleLanguage(file);
var extension = Path.GetExtension(file);
subtitleFiles.Add(new Tuple<string, Language, string>(file, language, extension));
}
var groupedSubtitleFiles = subtitleFiles.GroupBy(s => s.Item2 + s.Item3).ToList();
foreach (var group in groupedSubtitleFiles)
{
var groupCount = group.Count();
var copy = 1;
foreach (var file in group)
{
try
{
var path = file.Item1;
var language = file.Item2;
var extension = file.Item3;
var suffix = GetSuffix(language, copy, groupCount > 1);
var subtitleFile = ImportFile(localMovie.Movie, movieFile, path, isReadOnly, extension, suffix);
subtitleFile.Language = language;
_mediaFileAttributeService.SetFilePermissions(path);
_subtitleFileService.Upsert(subtitleFile);
importedFiles.Add(subtitleFile);
copy++;
}
catch (Exception ex)
{
_logger.Warn(ex, "Failed to import subtitle file: {0}", file.Item1);
}
}
}
return importedFiles;
}
private string GetSuffix(Language language, int copy, bool multipleCopies = false)