|
@@ -61,92 +61,92 @@ namespace Emby.Server.Implementations.FileOrganization
|
|
|
};
|
|
|
|
|
|
try
|
|
|
- {
|
|
|
- if (_libraryMonitor.IsPathLocked(path))
|
|
|
{
|
|
|
- result.Status = FileSortingStatus.Failure;
|
|
|
- result.StatusMessage = "Path is locked by other processes. Please try again later.";
|
|
|
- return result;
|
|
|
- }
|
|
|
+ if (_libraryMonitor.IsPathLocked(path))
|
|
|
+ {
|
|
|
+ result.Status = FileSortingStatus.Failure;
|
|
|
+ result.StatusMessage = "Path is locked by other processes. Please try again later.";
|
|
|
+ return result;
|
|
|
+ }
|
|
|
|
|
|
- var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
|
|
|
- var resolver = new EpisodeResolver(namingOptions, new NullLogger());
|
|
|
+ var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
|
|
|
+ var resolver = new EpisodeResolver(namingOptions, new NullLogger());
|
|
|
|
|
|
- var episodeInfo = resolver.Resolve(path, false) ??
|
|
|
- new MediaBrowser.Naming.TV.EpisodeInfo();
|
|
|
+ var episodeInfo = resolver.Resolve(path, false) ??
|
|
|
+ new MediaBrowser.Naming.TV.EpisodeInfo();
|
|
|
|
|
|
- var seriesName = episodeInfo.SeriesName;
|
|
|
+ var seriesName = episodeInfo.SeriesName;
|
|
|
|
|
|
- if (!string.IsNullOrEmpty(seriesName))
|
|
|
- {
|
|
|
- var seasonNumber = episodeInfo.SeasonNumber;
|
|
|
+ if (!string.IsNullOrEmpty(seriesName))
|
|
|
+ {
|
|
|
+ var seasonNumber = episodeInfo.SeasonNumber;
|
|
|
|
|
|
- result.ExtractedSeasonNumber = seasonNumber;
|
|
|
+ result.ExtractedSeasonNumber = seasonNumber;
|
|
|
|
|
|
- // Passing in true will include a few extra regex's
|
|
|
- var episodeNumber = episodeInfo.EpisodeNumber;
|
|
|
+ // Passing in true will include a few extra regex's
|
|
|
+ var episodeNumber = episodeInfo.EpisodeNumber;
|
|
|
|
|
|
- result.ExtractedEpisodeNumber = episodeNumber;
|
|
|
+ result.ExtractedEpisodeNumber = episodeNumber;
|
|
|
|
|
|
- var premiereDate = episodeInfo.IsByDate ?
|
|
|
- new DateTime(episodeInfo.Year.Value, episodeInfo.Month.Value, episodeInfo.Day.Value) :
|
|
|
- (DateTime?)null;
|
|
|
+ var premiereDate = episodeInfo.IsByDate ?
|
|
|
+ new DateTime(episodeInfo.Year.Value, episodeInfo.Month.Value, episodeInfo.Day.Value) :
|
|
|
+ (DateTime?)null;
|
|
|
|
|
|
- if (episodeInfo.IsByDate || (seasonNumber.HasValue && episodeNumber.HasValue))
|
|
|
- {
|
|
|
- if (episodeInfo.IsByDate)
|
|
|
+ if (episodeInfo.IsByDate || (seasonNumber.HasValue && episodeNumber.HasValue))
|
|
|
{
|
|
|
- _logger.Debug("Extracted information from {0}. Series name {1}, Date {2}", path, seriesName, premiereDate.Value);
|
|
|
+ if (episodeInfo.IsByDate)
|
|
|
+ {
|
|
|
+ _logger.Debug("Extracted information from {0}. Series name {1}, Date {2}", path, seriesName, premiereDate.Value);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ _logger.Debug("Extracted information from {0}. Series name {1}, Season {2}, Episode {3}", path, seriesName, seasonNumber, episodeNumber);
|
|
|
+ }
|
|
|
+
|
|
|
+ var endingEpisodeNumber = episodeInfo.EndingEpsiodeNumber;
|
|
|
+
|
|
|
+ result.ExtractedEndingEpisodeNumber = endingEpisodeNumber;
|
|
|
+
|
|
|
+ await OrganizeEpisode(path,
|
|
|
+ seriesName,
|
|
|
+ seasonNumber,
|
|
|
+ episodeNumber,
|
|
|
+ endingEpisodeNumber,
|
|
|
+ premiereDate,
|
|
|
+ options,
|
|
|
+ overwriteExisting,
|
|
|
+ false,
|
|
|
+ result,
|
|
|
+ cancellationToken).ConfigureAwait(false);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- _logger.Debug("Extracted information from {0}. Series name {1}, Season {2}, Episode {3}", path, seriesName, seasonNumber, episodeNumber);
|
|
|
+ var msg = string.Format("Unable to determine episode number from {0}", path);
|
|
|
+ result.Status = FileSortingStatus.Failure;
|
|
|
+ result.StatusMessage = msg;
|
|
|
+ _logger.Warn(msg);
|
|
|
}
|
|
|
-
|
|
|
- var endingEpisodeNumber = episodeInfo.EndingEpsiodeNumber;
|
|
|
-
|
|
|
- result.ExtractedEndingEpisodeNumber = endingEpisodeNumber;
|
|
|
-
|
|
|
- await OrganizeEpisode(path,
|
|
|
- seriesName,
|
|
|
- seasonNumber,
|
|
|
- episodeNumber,
|
|
|
- endingEpisodeNumber,
|
|
|
- premiereDate,
|
|
|
- options,
|
|
|
- overwriteExisting,
|
|
|
- false,
|
|
|
- result,
|
|
|
- cancellationToken).ConfigureAwait(false);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- var msg = string.Format("Unable to determine episode number from {0}", path);
|
|
|
+ var msg = string.Format("Unable to determine series name from {0}", path);
|
|
|
result.Status = FileSortingStatus.Failure;
|
|
|
result.StatusMessage = msg;
|
|
|
_logger.Warn(msg);
|
|
|
}
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- var msg = string.Format("Unable to determine series name from {0}", path);
|
|
|
- result.Status = FileSortingStatus.Failure;
|
|
|
- result.StatusMessage = msg;
|
|
|
- _logger.Warn(msg);
|
|
|
- }
|
|
|
|
|
|
- var previousResult = _organizationService.GetResultBySourcePath(path);
|
|
|
+ var previousResult = _organizationService.GetResultBySourcePath(path);
|
|
|
|
|
|
- if (previousResult != null)
|
|
|
- {
|
|
|
- // Don't keep saving the same result over and over if nothing has changed
|
|
|
- if (previousResult.Status == result.Status && previousResult.StatusMessage == result.StatusMessage && result.Status != FileSortingStatus.Success)
|
|
|
+ if (previousResult != null)
|
|
|
{
|
|
|
- return previousResult;
|
|
|
+ // Don't keep saving the same result over and over if nothing has changed
|
|
|
+ if (previousResult.Status == result.Status && previousResult.StatusMessage == result.StatusMessage && result.Status != FileSortingStatus.Success)
|
|
|
+ {
|
|
|
+ return previousResult;
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- await _organizationService.SaveResult(result, CancellationToken.None).ConfigureAwait(false);
|
|
|
+ await _organizationService.SaveResult(result, CancellationToken.None).ConfigureAwait(false);
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
@@ -162,58 +162,60 @@ namespace Emby.Server.Implementations.FileOrganization
|
|
|
var result = _organizationService.GetResult(request.ResultId);
|
|
|
|
|
|
try
|
|
|
- {
|
|
|
- Series series = null;
|
|
|
-
|
|
|
- if (request.NewSeriesProviderIds.Count > 0)
|
|
|
{
|
|
|
- // We're having a new series here
|
|
|
- SeriesInfo seriesRequest = new SeriesInfo();
|
|
|
- seriesRequest.ProviderIds = request.NewSeriesProviderIds;
|
|
|
-
|
|
|
- var refreshOptions = new MetadataRefreshOptions(_fileSystem);
|
|
|
- series = new Series();
|
|
|
- series.Id = Guid.NewGuid();
|
|
|
- series.Name = request.NewSeriesName;
|
|
|
+ Series series = null;
|
|
|
|
|
|
- int year;
|
|
|
- if (int.TryParse(request.NewSeriesYear, out year))
|
|
|
+ if (request.NewSeriesProviderIds.Count > 0)
|
|
|
{
|
|
|
- series.ProductionYear = year;
|
|
|
- }
|
|
|
+ // We're having a new series here
|
|
|
+ SeriesInfo seriesRequest = new SeriesInfo();
|
|
|
+ seriesRequest.ProviderIds = request.NewSeriesProviderIds;
|
|
|
|
|
|
- var seriesFolderName = series.Name;
|
|
|
- if (series.ProductionYear.HasValue)
|
|
|
- {
|
|
|
- seriesFolderName = string.Format("{0} ({1})", seriesFolderName, series.ProductionYear);
|
|
|
- }
|
|
|
+ var refreshOptions = new MetadataRefreshOptions(_fileSystem);
|
|
|
+ series = new Series();
|
|
|
+ series.Id = Guid.NewGuid();
|
|
|
+ series.Name = request.NewSeriesName;
|
|
|
+
|
|
|
+ int year;
|
|
|
+ if (int.TryParse(request.NewSeriesYear, out year))
|
|
|
+ {
|
|
|
+ series.ProductionYear = year;
|
|
|
+ }
|
|
|
|
|
|
- series.Path = Path.Combine(request.TargetFolder, seriesFolderName);
|
|
|
+ var seriesFolderName = series.Name;
|
|
|
+ if (series.ProductionYear.HasValue)
|
|
|
+ {
|
|
|
+ seriesFolderName = string.Format("{0} ({1})", seriesFolderName, series.ProductionYear);
|
|
|
+ }
|
|
|
|
|
|
- series.ProviderIds = request.NewSeriesProviderIds;
|
|
|
+ seriesFolderName = _fileSystem.GetValidFilename(seriesFolderName);
|
|
|
|
|
|
- await series.RefreshMetadata(refreshOptions, cancellationToken);
|
|
|
- }
|
|
|
+ series.Path = Path.Combine(request.TargetFolder, seriesFolderName);
|
|
|
|
|
|
- if (series == null)
|
|
|
- {
|
|
|
- // Existing Series
|
|
|
- series = (Series)_libraryManager.GetItemById(new Guid(request.SeriesId));
|
|
|
- }
|
|
|
+ series.ProviderIds = request.NewSeriesProviderIds;
|
|
|
|
|
|
- await OrganizeEpisode(result.OriginalPath,
|
|
|
- series,
|
|
|
- request.SeasonNumber,
|
|
|
- request.EpisodeNumber,
|
|
|
- request.EndingEpisodeNumber,
|
|
|
- null,
|
|
|
- options,
|
|
|
- true,
|
|
|
- request.RememberCorrection,
|
|
|
- result,
|
|
|
- cancellationToken).ConfigureAwait(false);
|
|
|
+ await series.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (series == null)
|
|
|
+ {
|
|
|
+ // Existing Series
|
|
|
+ series = (Series)_libraryManager.GetItemById(new Guid(request.SeriesId));
|
|
|
+ }
|
|
|
+
|
|
|
+ await OrganizeEpisode(result.OriginalPath,
|
|
|
+ series,
|
|
|
+ request.SeasonNumber,
|
|
|
+ request.EpisodeNumber,
|
|
|
+ request.EndingEpisodeNumber,
|
|
|
+ null,
|
|
|
+ options,
|
|
|
+ true,
|
|
|
+ request.RememberCorrection,
|
|
|
+ result,
|
|
|
+ cancellationToken).ConfigureAwait(false);
|
|
|
|
|
|
- await _organizationService.SaveResult(result, CancellationToken.None).ConfigureAwait(false);
|
|
|
+ await _organizationService.SaveResult(result, CancellationToken.None).ConfigureAwait(false);
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
@@ -287,91 +289,91 @@ namespace Emby.Server.Implementations.FileOrganization
|
|
|
{
|
|
|
throw new Exception("File is currently processed otherwise. Please try again later.");
|
|
|
}
|
|
|
-
|
|
|
- try
|
|
|
- {
|
|
|
- // Proceed to sort the file
|
|
|
- var newPath = await GetNewPath(sourcePath, series, seasonNumber, episodeNumber, endingEpiosdeNumber, premiereDate, options.TvOptions, cancellationToken).ConfigureAwait(false);
|
|
|
-
|
|
|
- if (string.IsNullOrEmpty(newPath))
|
|
|
- {
|
|
|
- var msg = string.Format("Unable to sort {0} because target path could not be determined.", sourcePath);
|
|
|
- throw new Exception(msg);
|
|
|
- }
|
|
|
-
|
|
|
- _logger.Info("Sorting file {0} to new path {1}", sourcePath, newPath);
|
|
|
- result.TargetPath = newPath;
|
|
|
-
|
|
|
- var fileExists = _fileSystem.FileExists(result.TargetPath);
|
|
|
- var otherDuplicatePaths = GetOtherDuplicatePaths(result.TargetPath, series, seasonNumber, episodeNumber, endingEpiosdeNumber);
|
|
|
|
|
|
- if (!overwriteExisting)
|
|
|
+ try
|
|
|
{
|
|
|
- if (options.TvOptions.CopyOriginalFile && fileExists && IsSameEpisode(sourcePath, newPath))
|
|
|
- {
|
|
|
- var msg = string.Format("File '{0}' already copied to new path '{1}', stopping organization", sourcePath, newPath);
|
|
|
- _logger.Info(msg);
|
|
|
- result.Status = FileSortingStatus.SkippedExisting;
|
|
|
- result.StatusMessage = msg;
|
|
|
- return;
|
|
|
- }
|
|
|
+ // Proceed to sort the file
|
|
|
+ var newPath = await GetNewPath(sourcePath, series, seasonNumber, episodeNumber, endingEpiosdeNumber, premiereDate, options.TvOptions, cancellationToken).ConfigureAwait(false);
|
|
|
|
|
|
- if (fileExists)
|
|
|
+ if (string.IsNullOrEmpty(newPath))
|
|
|
{
|
|
|
- var msg = string.Format("File '{0}' already exists as '{1}', stopping organization", sourcePath, newPath);
|
|
|
- _logger.Info(msg);
|
|
|
- result.Status = FileSortingStatus.SkippedExisting;
|
|
|
- result.StatusMessage = msg;
|
|
|
- result.TargetPath = newPath;
|
|
|
- return;
|
|
|
+ var msg = string.Format("Unable to sort {0} because target path could not be determined.", sourcePath);
|
|
|
+ throw new Exception(msg);
|
|
|
}
|
|
|
|
|
|
- if (otherDuplicatePaths.Count > 0)
|
|
|
- {
|
|
|
- var msg = string.Format("File '{0}' already exists as these:'{1}'. Stopping organization", sourcePath, string.Join("', '", otherDuplicatePaths));
|
|
|
- _logger.Info(msg);
|
|
|
- result.Status = FileSortingStatus.SkippedExisting;
|
|
|
- result.StatusMessage = msg;
|
|
|
- result.DuplicatePaths = otherDuplicatePaths;
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
+ _logger.Info("Sorting file {0} to new path {1}", sourcePath, newPath);
|
|
|
+ result.TargetPath = newPath;
|
|
|
|
|
|
- PerformFileSorting(options.TvOptions, result);
|
|
|
-
|
|
|
- if (overwriteExisting)
|
|
|
- {
|
|
|
- var hasRenamedFiles = false;
|
|
|
+ var fileExists = _fileSystem.FileExists(result.TargetPath);
|
|
|
+ var otherDuplicatePaths = GetOtherDuplicatePaths(result.TargetPath, series, seasonNumber, episodeNumber, endingEpiosdeNumber);
|
|
|
|
|
|
- foreach (var path in otherDuplicatePaths)
|
|
|
+ if (!overwriteExisting)
|
|
|
{
|
|
|
- _logger.Debug("Removing duplicate episode {0}", path);
|
|
|
-
|
|
|
- _libraryMonitor.ReportFileSystemChangeBeginning(path);
|
|
|
-
|
|
|
- var renameRelatedFiles = !hasRenamedFiles &&
|
|
|
- string.Equals(Path.GetDirectoryName(path), Path.GetDirectoryName(result.TargetPath), StringComparison.OrdinalIgnoreCase);
|
|
|
-
|
|
|
- if (renameRelatedFiles)
|
|
|
+ if (options.TvOptions.CopyOriginalFile && fileExists && IsSameEpisode(sourcePath, newPath))
|
|
|
{
|
|
|
- hasRenamedFiles = true;
|
|
|
+ var msg = string.Format("File '{0}' already copied to new path '{1}', stopping organization", sourcePath, newPath);
|
|
|
+ _logger.Info(msg);
|
|
|
+ result.Status = FileSortingStatus.SkippedExisting;
|
|
|
+ result.StatusMessage = msg;
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- try
|
|
|
+ if (fileExists)
|
|
|
{
|
|
|
- DeleteLibraryFile(path, renameRelatedFiles, result.TargetPath);
|
|
|
+ var msg = string.Format("File '{0}' already exists as '{1}', stopping organization", sourcePath, newPath);
|
|
|
+ _logger.Info(msg);
|
|
|
+ result.Status = FileSortingStatus.SkippedExisting;
|
|
|
+ result.StatusMessage = msg;
|
|
|
+ result.TargetPath = newPath;
|
|
|
+ return;
|
|
|
}
|
|
|
- catch (IOException ex)
|
|
|
+
|
|
|
+ if (otherDuplicatePaths.Count > 0)
|
|
|
{
|
|
|
- _logger.ErrorException("Error removing duplicate episode", ex, path);
|
|
|
+ var msg = string.Format("File '{0}' already exists as these:'{1}'. Stopping organization", sourcePath, string.Join("', '", otherDuplicatePaths));
|
|
|
+ _logger.Info(msg);
|
|
|
+ result.Status = FileSortingStatus.SkippedExisting;
|
|
|
+ result.StatusMessage = msg;
|
|
|
+ result.DuplicatePaths = otherDuplicatePaths;
|
|
|
+ return;
|
|
|
}
|
|
|
- finally
|
|
|
+ }
|
|
|
+
|
|
|
+ PerformFileSorting(options.TvOptions, result);
|
|
|
+
|
|
|
+ if (overwriteExisting)
|
|
|
+ {
|
|
|
+ var hasRenamedFiles = false;
|
|
|
+
|
|
|
+ foreach (var path in otherDuplicatePaths)
|
|
|
{
|
|
|
- _libraryMonitor.ReportFileSystemChangeComplete(path, true);
|
|
|
+ _logger.Debug("Removing duplicate episode {0}", path);
|
|
|
+
|
|
|
+ _libraryMonitor.ReportFileSystemChangeBeginning(path);
|
|
|
+
|
|
|
+ var renameRelatedFiles = !hasRenamedFiles &&
|
|
|
+ string.Equals(Path.GetDirectoryName(path), Path.GetDirectoryName(result.TargetPath), StringComparison.OrdinalIgnoreCase);
|
|
|
+
|
|
|
+ if (renameRelatedFiles)
|
|
|
+ {
|
|
|
+ hasRenamedFiles = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ DeleteLibraryFile(path, renameRelatedFiles, result.TargetPath);
|
|
|
+ }
|
|
|
+ catch (IOException ex)
|
|
|
+ {
|
|
|
+ _logger.ErrorException("Error removing duplicate episode", ex, path);
|
|
|
+ }
|
|
|
+ finally
|
|
|
+ {
|
|
|
+ _libraryMonitor.ReportFileSystemChangeComplete(path, true);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
result.Status = FileSortingStatus.Failure;
|