aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Server.Implementations/FileOrganization
diff options
context:
space:
mode:
authorLuke <luke.pulverenti@gmail.com>2015-10-26 18:50:19 -0400
committerLuke <luke.pulverenti@gmail.com>2015-10-26 18:50:19 -0400
commit35778ebc02e5931142a1fe31a256b7488a07c5c2 (patch)
treeced0290be8820f5e507b51ca4c5165212b1879d1 /MediaBrowser.Server.Implementations/FileOrganization
parentc0dc8d055bfd4d2f58591083beb9e9128357aad6 (diff)
parent8d77308593c3b16b733b0109323770d9dfe7e166 (diff)
Merge pull request #1222 from MediaBrowser/dev
3.0.5768.7
Diffstat (limited to 'MediaBrowser.Server.Implementations/FileOrganization')
-rw-r--r--MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs76
-rw-r--r--MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs1
-rw-r--r--MediaBrowser.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs1
-rw-r--r--MediaBrowser.Server.Implementations/FileOrganization/TvFolderOrganizer.cs80
4 files changed, 108 insertions, 50 deletions
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
index 06b72e4ef..e36813d11 100644
--- a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
+++ b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
@@ -17,6 +17,7 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using CommonIO;
namespace MediaBrowser.Server.Implementations.FileOrganization
{
@@ -182,7 +183,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
_logger.Info("Sorting file {0} to new path {1}", sourcePath, newPath);
result.TargetPath = newPath;
- var fileExists = File.Exists(result.TargetPath);
+ var fileExists = _fileSystem.FileExists(result.TargetPath);
var otherDuplicatePaths = GetOtherDuplicatePaths(result.TargetPath, series, seasonNumber, episodeNumber, endingEpiosdeNumber);
if (!overwriteExisting)
@@ -256,7 +257,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
if (!string.IsNullOrWhiteSpace(originalFilenameWithoutExtension) && !string.IsNullOrWhiteSpace(directory))
{
// Get all related files, e.g. metadata, images, etc
- var files = Directory.EnumerateFiles(directory, "*", SearchOption.TopDirectoryOnly)
+ var files = _fileSystem.GetFilePaths(directory)
.Where(i => (Path.GetFileNameWithoutExtension(i) ?? string.Empty).StartsWith(originalFilenameWithoutExtension, StringComparison.OrdinalIgnoreCase))
.ToList();
@@ -272,7 +273,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
var destination = Path.Combine(directory, filename);
- File.Move(file, destination);
+ _fileSystem.MoveFile(file, destination);
}
}
}
@@ -313,7 +314,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
try
{
- var filesOfOtherExtensions = Directory.EnumerateFiles(folder, "*", SearchOption.TopDirectoryOnly)
+ var filesOfOtherExtensions = _fileSystem.GetFilePaths(folder)
.Where(i => _libraryManager.IsVideoFile(i) && string.Equals(_fileSystem.GetFileNameWithoutExtension(i), targetFileNameWithoutExtension, StringComparison.OrdinalIgnoreCase));
episodePaths.AddRange(filesOfOtherExtensions);
@@ -332,19 +333,19 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
{
_libraryMonitor.ReportFileSystemChangeBeginning(result.TargetPath);
- Directory.CreateDirectory(Path.GetDirectoryName(result.TargetPath));
+ _fileSystem.CreateDirectory(Path.GetDirectoryName(result.TargetPath));
- var targetAlreadyExists = File.Exists(result.TargetPath);
+ var targetAlreadyExists = _fileSystem.FileExists(result.TargetPath);
try
{
if (targetAlreadyExists || options.CopyOriginalFile)
{
- File.Copy(result.OriginalPath, result.TargetPath, true);
+ _fileSystem.CopyFile(result.OriginalPath, result.TargetPath, true);
}
else
{
- File.Move(result.OriginalPath, result.TargetPath);
+ _fileSystem.MoveFile(result.OriginalPath, result.TargetPath);
}
result.Status = FileSortingStatus.Success;
@@ -435,7 +436,26 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
var newPath = GetSeasonFolderPath(series, seasonNumber, options);
- var episodeFileName = GetEpisodeFileName(sourcePath, series.Name, seasonNumber, episodeNumber, endingEpisodeNumber, episode.Name, options);
+ // MAX_PATH - trailing <NULL> charachter - drive component: 260 - 1 - 3 = 256
+ // Usually newPath would include the drive component, but use 256 to be sure
+ var maxFilenameLength = 256 - newPath.Length;
+
+ if (!newPath.EndsWith(@"\"))
+ {
+ // Remove 1 for missing backslash combining path and filename
+ maxFilenameLength--;
+ }
+
+ // Remove additional 4 chars to prevent PathTooLongException for downloaded subtitles (eg. filename.ext.eng.srt)
+ maxFilenameLength -= 4;
+
+ var episodeFileName = GetEpisodeFileName(sourcePath, series.Name, seasonNumber, episodeNumber, endingEpisodeNumber, episode.Name, options, maxFilenameLength);
+
+ if (string.IsNullOrEmpty(episodeFileName))
+ {
+ // cause failure
+ return string.Empty;
+ }
newPath = Path.Combine(newPath, episodeFileName);
@@ -481,7 +501,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
return Path.Combine(path, _fileSystem.GetValidFilename(seasonFolderName));
}
- private string GetEpisodeFileName(string sourcePath, string seriesName, int seasonNumber, int episodeNumber, int? endingEpisodeNumber, string episodeTitle, TvFileOrganizationOptions options)
+ private string GetEpisodeFileName(string sourcePath, string seriesName, int seasonNumber, int episodeNumber, int? endingEpisodeNumber, string episodeTitle, TvFileOrganizationOptions options, int? maxLength)
{
seriesName = _fileSystem.GetValidFilename(seriesName).Trim();
episodeTitle = _fileSystem.GetValidFilename(episodeTitle).Trim();
@@ -497,9 +517,9 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
.Replace("%0s", seasonNumber.ToString("00", _usCulture))
.Replace("%00s", seasonNumber.ToString("000", _usCulture))
.Replace("%ext", sourceExtension)
- .Replace("%en", episodeTitle)
- .Replace("%e.n", episodeTitle.Replace(" ", "."))
- .Replace("%e_n", episodeTitle.Replace(" ", "_"));
+ .Replace("%en", "%#1")
+ .Replace("%e.n", "%#2")
+ .Replace("%e_n", "%#3");
if (endingEpisodeNumber.HasValue)
{
@@ -508,9 +528,37 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
.Replace("%00ed", endingEpisodeNumber.Value.ToString("000", _usCulture));
}
- return result.Replace("%e", episodeNumber.ToString(_usCulture))
+ result = result.Replace("%e", episodeNumber.ToString(_usCulture))
.Replace("%0e", episodeNumber.ToString("00", _usCulture))
.Replace("%00e", episodeNumber.ToString("000", _usCulture));
+
+ if (maxLength.HasValue && result.Contains("%#"))
+ {
+ // Substract 3 for the temp token length (%#1, %#2 or %#3)
+ int maxRemainingTitleLength = maxLength.Value - result.Length + 3;
+ string shortenedEpisodeTitle = string.Empty;
+
+ if (maxRemainingTitleLength > 5)
+ {
+ // A title with fewer than 5 letters wouldn't be of much value
+ shortenedEpisodeTitle = episodeTitle.Substring(0, Math.Min(maxRemainingTitleLength, episodeTitle.Length));
+ }
+
+ result = result.Replace("%#1", shortenedEpisodeTitle)
+ .Replace("%#2", shortenedEpisodeTitle.Replace(" ", "."))
+ .Replace("%#3", shortenedEpisodeTitle.Replace(" ", "_"));
+ }
+
+ if (maxLength.HasValue && result.Length > maxLength.Value)
+ {
+ // There may be cases where reducing the title length may still not be sufficient to
+ // stay below maxLength
+ var msg = string.Format("Unable to generate an episode file name shorter than {0} characters to constrain to the max path limit", maxLength);
+ _logger.Warn(msg);
+ return string.Empty;
+ }
+
+ return result;
}
private bool IsSameEpisode(string sourcePath, string newPath)
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs b/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs
index a6116ab09..839a85adb 100644
--- a/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs
+++ b/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs
@@ -13,6 +13,7 @@ using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
+using CommonIO;
namespace MediaBrowser.Server.Implementations.FileOrganization
{
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs b/MediaBrowser.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs
index f993e1fc3..f1fe5539f 100644
--- a/MediaBrowser.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs
+++ b/MediaBrowser.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs
@@ -10,6 +10,7 @@ using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
+using CommonIO;
namespace MediaBrowser.Server.Implementations.FileOrganization
{
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/TvFolderOrganizer.cs b/MediaBrowser.Server.Implementations/FileOrganization/TvFolderOrganizer.cs
index 0caa8c26e..3e5296639 100644
--- a/MediaBrowser.Server.Implementations/FileOrganization/TvFolderOrganizer.cs
+++ b/MediaBrowser.Server.Implementations/FileOrganization/TvFolderOrganizer.cs
@@ -11,6 +11,7 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using CommonIO;
namespace MediaBrowser.Server.Implementations.FileOrganization
{
@@ -35,17 +36,33 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
_providerManager = providerManager;
}
- public async Task Organize(TvFileOrganizationOptions options, CancellationToken cancellationToken, IProgress<double> progress)
+ private bool EnableOrganization(FileSystemMetadata fileInfo, TvFileOrganizationOptions options)
{
var minFileBytes = options.MinFileSizeMb * 1024 * 1024;
+ try
+ {
+ return _libraryManager.IsVideoFile(fileInfo.FullName) && fileInfo.Length >= minFileBytes;
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error organizing file {0}", ex, fileInfo.Name);
+ }
+
+ return false;
+ }
+
+ public async Task Organize(TvFileOrganizationOptions options, CancellationToken cancellationToken, IProgress<double> progress)
+ {
var watchLocations = options.WatchLocations.ToList();
var eligibleFiles = watchLocations.SelectMany(GetFilesToOrganize)
.OrderBy(_fileSystem.GetCreationTimeUtc)
- .Where(i => _libraryManager.IsVideoFile(i.FullName) && i.Length >= minFileBytes)
+ .Where(i => EnableOrganization(i, options))
.ToList();
+ var processedFolders = new HashSet<string>();
+
progress.Report(10);
if (eligibleFiles.Count > 0)
@@ -59,7 +76,11 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
try
{
- await organizer.OrganizeEpisodeFile(file.FullName, options, options.OverwriteExistingEpisodes, cancellationToken).ConfigureAwait(false);
+ var result = await organizer.OrganizeEpisodeFile(file.FullName, options, options.OverwriteExistingEpisodes, cancellationToken).ConfigureAwait(false);
+ if (result.Status == FileSortingStatus.Success && !processedFolders.Contains(file.DirectoryName, StringComparer.OrdinalIgnoreCase))
+ {
+ processedFolders.Add(file.DirectoryName);
+ }
}
catch (Exception ex)
{
@@ -77,7 +98,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
cancellationToken.ThrowIfCancellationRequested();
progress.Report(99);
- foreach (var path in watchLocations)
+ foreach (var path in processedFolders)
{
var deleteExtensions = options.LeftOverFileExtensionsToDelete
.Select(i => i.Trim().TrimStart('.'))
@@ -92,9 +113,9 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
if (options.DeleteEmptyFolders)
{
- foreach (var subfolder in GetDirectories(path).ToList())
+ if (!IsWatchFolder(path, watchLocations))
{
- DeleteEmptyFolders(subfolder);
+ DeleteEmptyFolders(path);
}
}
}
@@ -103,44 +124,22 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
}
/// <summary>
- /// Gets the directories.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>IEnumerable{System.String}.</returns>
- private IEnumerable<string> GetDirectories(string path)
- {
- try
- {
- return Directory
- .EnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly)
- .ToList();
- }
- catch (IOException ex)
- {
- _logger.ErrorException("Error getting files from {0}", ex, path);
-
- return new List<string>();
- }
- }
-
- /// <summary>
/// Gets the files to organize.
/// </summary>
/// <param name="path">The path.</param>
/// <returns>IEnumerable{FileInfo}.</returns>
- private IEnumerable<FileInfo> GetFilesToOrganize(string path)
+ private List<FileSystemMetadata> GetFilesToOrganize(string path)
{
try
{
- return new DirectoryInfo(path)
- .EnumerateFiles("*", SearchOption.AllDirectories)
+ return _fileSystem.GetFiles(path, true)
.ToList();
}
catch (IOException ex)
{
_logger.ErrorException("Error getting files from {0}", ex, path);
- return new List<FileInfo>();
+ return new List<FileSystemMetadata>();
}
}
@@ -151,8 +150,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
/// <param name="extensions">The extensions.</param>
private void DeleteLeftOverFiles(string path, IEnumerable<string> extensions)
{
- var eligibleFiles = new DirectoryInfo(path)
- .EnumerateFiles("*", SearchOption.AllDirectories)
+ var eligibleFiles = _fileSystem.GetFiles(path, true)
.Where(i => extensions.Contains(i.Extension, StringComparer.OrdinalIgnoreCase))
.ToList();
@@ -177,19 +175,19 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
{
try
{
- foreach (var d in Directory.EnumerateDirectories(path))
+ foreach (var d in _fileSystem.GetDirectoryPaths(path))
{
DeleteEmptyFolders(d);
}
- var entries = Directory.EnumerateFileSystemEntries(path);
+ var entries = _fileSystem.GetFileSystemEntryPaths(path);
if (!entries.Any())
{
try
{
_logger.Debug("Deleting empty directory {0}", path);
- Directory.Delete(path);
+ _fileSystem.DeleteDirectory(path, false);
}
catch (UnauthorizedAccessException) { }
catch (DirectoryNotFoundException) { }
@@ -197,5 +195,15 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
}
catch (UnauthorizedAccessException) { }
}
+
+ /// <summary>
+ /// Determines if a given folder path is contained in a folder list
+ /// </summary>
+ /// <param name="path">The folder path to check.</param>
+ /// <param name="watchLocations">A list of folders.</param>
+ private bool IsWatchFolder(string path, IEnumerable<string> watchLocations)
+ {
+ return watchLocations.Contains(path, StringComparer.OrdinalIgnoreCase);
+ }
}
}