mirror of https://github.com/jellyfin/jellyfin.git
Auto-Organize: Added feature to remember/persist series matching in manual organization dialog #2
When a filename cannot be auto-matched to an existing series name, the organization must be performed manually. Unfortunately not just once, but again and again for each episode coming in. This change proposes a simple but solid method to optionally persist the matching condition from within the manual organization dialog. This approach will make Emby "learn" how to organize files in the future without user interaction.
This commit is contained in:
parent
d28ef71d93
commit
3a868e28b3
|
@ -74,6 +74,34 @@ namespace MediaBrowser.Api.Library
|
||||||
public bool RememberCorrection { get; set; }
|
public bool RememberCorrection { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Route("/Library/FileOrganizationSmartMatch", "GET", Summary = "Gets smart match entries")]
|
||||||
|
public class GetSmartMatchInfos : IReturn<QueryResult<SmartMatchInfo>>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Skips over a given number of items within the results. Use for paging.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The start index.</value>
|
||||||
|
[ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||||
|
public int? StartIndex { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum number of items to return
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The limit.</value>
|
||||||
|
[ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||||
|
public int? Limit { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Route("/Library/FileOrganizationSmartMatch/{Id}/Delete", "POST", Summary = "Deletes a smart match entry")]
|
||||||
|
public class DeleteSmartMatchEntry
|
||||||
|
{
|
||||||
|
[ApiMember(Name = "Id", Description = "Item ID", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
[ApiMember(Name = "MatchString", Description = "SmartMatch String", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||||
|
public string MatchString { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
[Authenticated(Roles = "Admin")]
|
[Authenticated(Roles = "Admin")]
|
||||||
public class FileOrganizationService : BaseApiService
|
public class FileOrganizationService : BaseApiService
|
||||||
{
|
{
|
||||||
|
@ -130,5 +158,21 @@ namespace MediaBrowser.Api.Library
|
||||||
|
|
||||||
Task.WaitAll(task);
|
Task.WaitAll(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public object Get(GetSmartMatchInfos request)
|
||||||
|
{
|
||||||
|
var result = _iFileOrganizationService.GetSmartMatchInfos(new FileOrganizationResultQuery
|
||||||
|
{
|
||||||
|
Limit = request.Limit,
|
||||||
|
StartIndex = request.StartIndex
|
||||||
|
});
|
||||||
|
|
||||||
|
return ToOptimizedSerializedResultUsingCache(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Post(DeleteSmartMatchEntry request)
|
||||||
|
{
|
||||||
|
_iFileOrganizationService.DeleteSmartMatchEntry(request.Id, request.MatchString);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,5 +67,19 @@ namespace MediaBrowser.Controller.FileOrganization
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
Task SaveResult(FileOrganizationResult result, CancellationToken cancellationToken);
|
Task SaveResult(FileOrganizationResult result, CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a list of smart match entries
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">The query.</param>
|
||||||
|
/// <returns>IEnumerable{SmartMatchInfo}.</returns>
|
||||||
|
QueryResult<SmartMatchInfo> GetSmartMatchInfos(FileOrganizationResultQuery query);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deletes a smart match entry.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Id">Item Id.</param>
|
||||||
|
/// <param name="matchString">The match string to delete.</param>
|
||||||
|
void DeleteSmartMatchEntry(string Id, string matchString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -668,6 +668,9 @@
|
||||||
<Compile Include="..\MediaBrowser.Model\FileOrganization\FileSortingStatus.cs">
|
<Compile Include="..\MediaBrowser.Model\FileOrganization\FileSortingStatus.cs">
|
||||||
<Link>FileOrganization\FileSortingStatus.cs</Link>
|
<Link>FileOrganization\FileSortingStatus.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="..\MediaBrowser.Model\FileOrganization\SmartMatchInfo.cs">
|
||||||
|
<Link>FileOrganization\SmartMatchInfo.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="..\MediaBrowser.Model\FileOrganization\TvFileOrganizationOptions.cs">
|
<Compile Include="..\MediaBrowser.Model\FileOrganization\TvFileOrganizationOptions.cs">
|
||||||
<Link>FileOrganization\TvFileOrganizationOptions.cs</Link>
|
<Link>FileOrganization\TvFileOrganizationOptions.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|
|
@ -633,6 +633,9 @@
|
||||||
<Compile Include="..\MediaBrowser.Model\FileOrganization\FileSortingStatus.cs">
|
<Compile Include="..\MediaBrowser.Model\FileOrganization\FileSortingStatus.cs">
|
||||||
<Link>FileOrganization\FileSortingStatus.cs</Link>
|
<Link>FileOrganization\FileSortingStatus.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="..\MediaBrowser.Model\FileOrganization\SmartMatchInfo.cs">
|
||||||
|
<Link>FileOrganization\SmartMatchInfo.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="..\MediaBrowser.Model\FileOrganization\TvFileOrganizationOptions.cs">
|
<Compile Include="..\MediaBrowser.Model\FileOrganization\TvFileOrganizationOptions.cs">
|
||||||
<Link>FileOrganization\TvFileOrganizationOptions.cs</Link>
|
<Link>FileOrganization\TvFileOrganizationOptions.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
namespace MediaBrowser.Model.FileOrganization
|
namespace MediaBrowser.Model.FileOrganization
|
||||||
{
|
{
|
||||||
public class AutoOrganizeOptions
|
public class AutoOrganizeOptions
|
||||||
|
@ -9,9 +10,16 @@ namespace MediaBrowser.Model.FileOrganization
|
||||||
/// <value>The tv options.</value>
|
/// <value>The tv options.</value>
|
||||||
public TvFileOrganizationOptions TvOptions { get; set; }
|
public TvFileOrganizationOptions TvOptions { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a list of smart match entries.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The smart match entries.</value>
|
||||||
|
public List<SmartMatchInfo> SmartMatchInfos { get; set; }
|
||||||
|
|
||||||
public AutoOrganizeOptions()
|
public AutoOrganizeOptions()
|
||||||
{
|
{
|
||||||
TvOptions = new TvFileOrganizationOptions();
|
TvOptions = new TvFileOrganizationOptions();
|
||||||
|
SmartMatchInfos = new List<SmartMatchInfo>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace MediaBrowser.Model.FileOrganization
|
||||||
|
{
|
||||||
|
public class SmartMatchInfo
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public FileOrganizerType OrganizerType { get; set; }
|
||||||
|
public List<string> MatchStrings { get; set; }
|
||||||
|
|
||||||
|
public SmartMatchInfo()
|
||||||
|
{
|
||||||
|
MatchStrings = new List<string>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -137,6 +137,7 @@
|
||||||
<Compile Include="Dto\MetadataEditorInfo.cs" />
|
<Compile Include="Dto\MetadataEditorInfo.cs" />
|
||||||
<Compile Include="Dto\NameIdPair.cs" />
|
<Compile Include="Dto\NameIdPair.cs" />
|
||||||
<Compile Include="Dto\NameValuePair.cs" />
|
<Compile Include="Dto\NameValuePair.cs" />
|
||||||
|
<Compile Include="FileOrganization\SmartMatchInfo.cs" />
|
||||||
<Compile Include="MediaInfo\LiveStreamRequest.cs" />
|
<Compile Include="MediaInfo\LiveStreamRequest.cs" />
|
||||||
<Compile Include="MediaInfo\LiveStreamResponse.cs" />
|
<Compile Include="MediaInfo\LiveStreamResponse.cs" />
|
||||||
<Compile Include="MediaInfo\PlaybackInfoRequest.cs" />
|
<Compile Include="MediaInfo\PlaybackInfoRequest.cs" />
|
||||||
|
|
|
@ -46,12 +46,12 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
||||||
|
|
||||||
public Task<FileOrganizationResult> OrganizeEpisodeFile(string path, CancellationToken cancellationToken)
|
public Task<FileOrganizationResult> OrganizeEpisodeFile(string path, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var options = _config.GetAutoOrganizeOptions().TvOptions;
|
var options = _config.GetAutoOrganizeOptions();
|
||||||
|
|
||||||
return OrganizeEpisodeFile(path, options, false, cancellationToken);
|
return OrganizeEpisodeFile(path, options, false, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<FileOrganizationResult> OrganizeEpisodeFile(string path, TvFileOrganizationOptions options, bool overwriteExisting, CancellationToken cancellationToken)
|
public async Task<FileOrganizationResult> OrganizeEpisodeFile(string path, AutoOrganizeOptions options, bool overwriteExisting, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_logger.Info("Sorting file {0}", path);
|
_logger.Info("Sorting file {0}", path);
|
||||||
|
|
||||||
|
@ -110,6 +110,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
||||||
premiereDate,
|
premiereDate,
|
||||||
options,
|
options,
|
||||||
overwriteExisting,
|
overwriteExisting,
|
||||||
|
false,
|
||||||
result,
|
result,
|
||||||
cancellationToken).ConfigureAwait(false);
|
cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
@ -145,7 +146,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<FileOrganizationResult> OrganizeWithCorrection(EpisodeFileOrganizationRequest request, TvFileOrganizationOptions options, CancellationToken cancellationToken)
|
public async Task<FileOrganizationResult> OrganizeWithCorrection(EpisodeFileOrganizationRequest request, AutoOrganizeOptions options, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var result = _organizationService.GetResult(request.ResultId);
|
var result = _organizationService.GetResult(request.ResultId);
|
||||||
|
|
||||||
|
@ -159,6 +160,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
||||||
null,
|
null,
|
||||||
options,
|
options,
|
||||||
true,
|
true,
|
||||||
|
request.RememberCorrection,
|
||||||
result,
|
result,
|
||||||
cancellationToken).ConfigureAwait(false);
|
cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
@ -173,12 +175,13 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
||||||
int? episodeNumber,
|
int? episodeNumber,
|
||||||
int? endingEpiosdeNumber,
|
int? endingEpiosdeNumber,
|
||||||
DateTime? premiereDate,
|
DateTime? premiereDate,
|
||||||
TvFileOrganizationOptions options,
|
AutoOrganizeOptions options,
|
||||||
bool overwriteExisting,
|
bool overwriteExisting,
|
||||||
|
bool rememberCorrection,
|
||||||
FileOrganizationResult result,
|
FileOrganizationResult result,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var series = GetMatchingSeries(seriesName, result);
|
var series = GetMatchingSeries(seriesName, result, options);
|
||||||
|
|
||||||
if (series == null)
|
if (series == null)
|
||||||
{
|
{
|
||||||
|
@ -197,6 +200,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
||||||
premiereDate,
|
premiereDate,
|
||||||
options,
|
options,
|
||||||
overwriteExisting,
|
overwriteExisting,
|
||||||
|
rememberCorrection,
|
||||||
result,
|
result,
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
}
|
}
|
||||||
|
@ -207,15 +211,18 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
||||||
int? episodeNumber,
|
int? episodeNumber,
|
||||||
int? endingEpiosdeNumber,
|
int? endingEpiosdeNumber,
|
||||||
DateTime? premiereDate,
|
DateTime? premiereDate,
|
||||||
TvFileOrganizationOptions options,
|
AutoOrganizeOptions options,
|
||||||
bool overwriteExisting,
|
bool overwriteExisting,
|
||||||
|
bool rememberCorrection,
|
||||||
FileOrganizationResult result,
|
FileOrganizationResult result,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_logger.Info("Sorting file {0} into series {1}", sourcePath, series.Path);
|
_logger.Info("Sorting file {0} into series {1}", sourcePath, series.Path);
|
||||||
|
|
||||||
|
var originalExtractedSeriesString = result.ExtractedName;
|
||||||
|
|
||||||
// Proceed to sort the file
|
// Proceed to sort the file
|
||||||
var newPath = await GetNewPath(sourcePath, series, seasonNumber, episodeNumber, endingEpiosdeNumber, premiereDate, options, cancellationToken).ConfigureAwait(false);
|
var newPath = await GetNewPath(sourcePath, series, seasonNumber, episodeNumber, endingEpiosdeNumber, premiereDate, options.TvOptions, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(newPath))
|
if (string.IsNullOrEmpty(newPath))
|
||||||
{
|
{
|
||||||
|
@ -234,7 +241,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
||||||
|
|
||||||
if (!overwriteExisting)
|
if (!overwriteExisting)
|
||||||
{
|
{
|
||||||
if (options.CopyOriginalFile && fileExists && IsSameEpisode(sourcePath, newPath))
|
if (options.TvOptions.CopyOriginalFile && fileExists && IsSameEpisode(sourcePath, newPath))
|
||||||
{
|
{
|
||||||
_logger.Info("File {0} already copied to new path {1}, stopping organization", sourcePath, newPath);
|
_logger.Info("File {0} already copied to new path {1}, stopping organization", sourcePath, newPath);
|
||||||
result.Status = FileSortingStatus.SkippedExisting;
|
result.Status = FileSortingStatus.SkippedExisting;
|
||||||
|
@ -251,7 +258,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PerformFileSorting(options, result);
|
PerformFileSorting(options.TvOptions, result);
|
||||||
|
|
||||||
if (overwriteExisting)
|
if (overwriteExisting)
|
||||||
{
|
{
|
||||||
|
@ -285,6 +292,31 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rememberCorrection)
|
||||||
|
{
|
||||||
|
SaveSmartMatchString(originalExtractedSeriesString, series, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveSmartMatchString(string matchString, Series series, AutoOrganizeOptions options)
|
||||||
|
{
|
||||||
|
SmartMatchInfo info = options.SmartMatchInfos.Find(i => i.Id == series.Id);
|
||||||
|
|
||||||
|
if (info == null)
|
||||||
|
{
|
||||||
|
info = new SmartMatchInfo();
|
||||||
|
info.Id = series.Id;
|
||||||
|
info.OrganizerType = FileOrganizerType.Episode;
|
||||||
|
info.Name = series.Name;
|
||||||
|
options.SmartMatchInfos.Add(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!info.MatchStrings.Contains(matchString, StringComparer.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
info.MatchStrings.Add(matchString);
|
||||||
|
_config.SaveAutoOrganizeOptions(options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DeleteLibraryFile(string path, bool renameRelatedFiles, string targetPath)
|
private void DeleteLibraryFile(string path, bool renameRelatedFiles, string targetPath)
|
||||||
|
@ -435,7 +467,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Series GetMatchingSeries(string seriesName, FileOrganizationResult result)
|
private Series GetMatchingSeries(string seriesName, FileOrganizationResult result, AutoOrganizeOptions options)
|
||||||
{
|
{
|
||||||
var parsedName = _libraryManager.ParseName(seriesName);
|
var parsedName = _libraryManager.ParseName(seriesName);
|
||||||
|
|
||||||
|
@ -445,13 +477,28 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
||||||
result.ExtractedName = nameWithoutYear;
|
result.ExtractedName = nameWithoutYear;
|
||||||
result.ExtractedYear = yearInName;
|
result.ExtractedYear = yearInName;
|
||||||
|
|
||||||
return _libraryManager.RootFolder.GetRecursiveChildren(i => i is Series)
|
var series = _libraryManager.RootFolder.GetRecursiveChildren(i => i is Series)
|
||||||
.Cast<Series>()
|
.Cast<Series>()
|
||||||
.Select(i => NameUtils.GetMatchScore(nameWithoutYear, yearInName, i))
|
.Select(i => NameUtils.GetMatchScore(nameWithoutYear, yearInName, i))
|
||||||
.Where(i => i.Item2 > 0)
|
.Where(i => i.Item2 > 0)
|
||||||
.OrderByDescending(i => i.Item2)
|
.OrderByDescending(i => i.Item2)
|
||||||
.Select(i => i.Item1)
|
.Select(i => i.Item1)
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
if (series == null)
|
||||||
|
{
|
||||||
|
SmartMatchInfo info = options.SmartMatchInfos.Where(e => e.MatchStrings.Contains(seriesName, StringComparer.OrdinalIgnoreCase)).FirstOrDefault();
|
||||||
|
|
||||||
|
if (info != null)
|
||||||
|
{
|
||||||
|
series = _libraryManager.RootFolder.GetRecursiveChildren(i => i is Series)
|
||||||
|
.Cast<Series>()
|
||||||
|
.Where(i => i.Id == info.Id)
|
||||||
|
.FirstOrDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return series ?? new Series();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -10,6 +10,10 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
||||||
{
|
{
|
||||||
return manager.GetConfiguration<AutoOrganizeOptions>("autoorganize");
|
return manager.GetConfiguration<AutoOrganizeOptions>("autoorganize");
|
||||||
}
|
}
|
||||||
|
public static void SaveAutoOrganizeOptions(this IConfigurationManager manager, AutoOrganizeOptions options)
|
||||||
|
{
|
||||||
|
manager.SaveConfiguration("autoorganize", options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AutoOrganizeOptionsFactory : IConfigurationFactory
|
public class AutoOrganizeOptionsFactory : IConfigurationFactory
|
||||||
|
|
|
@ -11,6 +11,7 @@ using MediaBrowser.Model.Logging;
|
||||||
using MediaBrowser.Model.Querying;
|
using MediaBrowser.Model.Querying;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CommonIO;
|
using CommonIO;
|
||||||
|
@ -96,9 +97,9 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
||||||
return _repo.Delete(resultId);
|
return _repo.Delete(resultId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TvFileOrganizationOptions GetTvOptions()
|
private AutoOrganizeOptions GetAutoOrganizeptions()
|
||||||
{
|
{
|
||||||
return _config.GetAutoOrganizeOptions().TvOptions;
|
return _config.GetAutoOrganizeOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task PerformOrganization(string resultId)
|
public async Task PerformOrganization(string resultId)
|
||||||
|
@ -113,7 +114,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
||||||
var organizer = new EpisodeFileOrganizer(this, _config, _fileSystem, _logger, _libraryManager,
|
var organizer = new EpisodeFileOrganizer(this, _config, _fileSystem, _logger, _libraryManager,
|
||||||
_libraryMonitor, _providerManager);
|
_libraryMonitor, _providerManager);
|
||||||
|
|
||||||
await organizer.OrganizeEpisodeFile(result.OriginalPath, GetTvOptions(), true, CancellationToken.None)
|
await organizer.OrganizeEpisodeFile(result.OriginalPath, GetAutoOrganizeptions(), true, CancellationToken.None)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +128,55 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
||||||
var organizer = new EpisodeFileOrganizer(this, _config, _fileSystem, _logger, _libraryManager,
|
var organizer = new EpisodeFileOrganizer(this, _config, _fileSystem, _logger, _libraryManager,
|
||||||
_libraryMonitor, _providerManager);
|
_libraryMonitor, _providerManager);
|
||||||
|
|
||||||
await organizer.OrganizeWithCorrection(request, GetTvOptions(), CancellationToken.None).ConfigureAwait(false);
|
await organizer.OrganizeWithCorrection(request, GetAutoOrganizeptions(), CancellationToken.None).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public QueryResult<SmartMatchInfo> GetSmartMatchInfos(FileOrganizationResultQuery query)
|
||||||
|
{
|
||||||
|
if (query == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("query");
|
||||||
|
}
|
||||||
|
|
||||||
|
var options = GetAutoOrganizeptions();
|
||||||
|
|
||||||
|
var items = options.SmartMatchInfos.Skip(query.StartIndex ?? 0).Take(query.Limit ?? Int32.MaxValue);
|
||||||
|
|
||||||
|
return new QueryResult<SmartMatchInfo>()
|
||||||
|
{
|
||||||
|
Items = items.ToArray(),
|
||||||
|
TotalRecordCount = items.Count()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteSmartMatchEntry(string IdString, string matchString)
|
||||||
|
{
|
||||||
|
Guid Id;
|
||||||
|
|
||||||
|
if (!Guid.TryParse(IdString, out Id))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("Id");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(matchString))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("matchString");
|
||||||
|
}
|
||||||
|
|
||||||
|
var options = GetAutoOrganizeptions();
|
||||||
|
|
||||||
|
SmartMatchInfo info = options.SmartMatchInfos.Find(i => i.Id == Id);
|
||||||
|
|
||||||
|
if (info != null && info.MatchStrings.Contains(matchString))
|
||||||
|
{
|
||||||
|
info.MatchStrings.Remove(matchString);
|
||||||
|
if (info.MatchStrings.Count == 0)
|
||||||
|
{
|
||||||
|
options.SmartMatchInfos.Remove(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
_config.SaveAutoOrganizeOptions(options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,17 +50,17 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
||||||
get { return "Library"; }
|
get { return "Library"; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private TvFileOrganizationOptions GetTvOptions()
|
private AutoOrganizeOptions GetAutoOrganizeOptions()
|
||||||
{
|
{
|
||||||
return _config.GetAutoOrganizeOptions().TvOptions;
|
return _config.GetAutoOrganizeOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
|
public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
|
||||||
{
|
{
|
||||||
if (GetTvOptions().IsEnabled)
|
if (GetAutoOrganizeOptions().TvOptions.IsEnabled)
|
||||||
{
|
{
|
||||||
await new TvFolderOrganizer(_libraryManager, _logger, _fileSystem, _libraryMonitor, _organizationService, _config, _providerManager)
|
await new TvFolderOrganizer(_libraryManager, _logger, _fileSystem, _libraryMonitor, _organizationService, _config, _providerManager)
|
||||||
.Organize(GetTvOptions(), cancellationToken, progress).ConfigureAwait(false);
|
.Organize(GetAutoOrganizeOptions(), cancellationToken, progress).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,12 +74,12 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
||||||
|
|
||||||
public bool IsHidden
|
public bool IsHidden
|
||||||
{
|
{
|
||||||
get { return !GetTvOptions().IsEnabled; }
|
get { return !GetAutoOrganizeOptions().TvOptions.IsEnabled; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsEnabled
|
public bool IsEnabled
|
||||||
{
|
{
|
||||||
get { return GetTvOptions().IsEnabled; }
|
get { return GetAutoOrganizeOptions().TvOptions.IsEnabled; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsActivityLogged
|
public bool IsActivityLogged
|
||||||
|
|
|
@ -38,7 +38,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
||||||
|
|
||||||
private bool EnableOrganization(FileSystemMetadata fileInfo, TvFileOrganizationOptions options)
|
private bool EnableOrganization(FileSystemMetadata fileInfo, TvFileOrganizationOptions options)
|
||||||
{
|
{
|
||||||
var minFileBytes = options.MinFileSizeMb * 1024 * 1024;
|
var minFileBytes = options.TvOptions.MinFileSizeMb * 1024 * 1024;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -52,9 +52,9 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Organize(TvFileOrganizationOptions options, CancellationToken cancellationToken, IProgress<double> progress)
|
public async Task Organize(AutoOrganizeOptions options, CancellationToken cancellationToken, IProgress<double> progress)
|
||||||
{
|
{
|
||||||
var watchLocations = options.WatchLocations.ToList();
|
var watchLocations = options.TvOptions.WatchLocations.ToList();
|
||||||
|
|
||||||
var eligibleFiles = watchLocations.SelectMany(GetFilesToOrganize)
|
var eligibleFiles = watchLocations.SelectMany(GetFilesToOrganize)
|
||||||
.OrderBy(_fileSystem.GetCreationTimeUtc)
|
.OrderBy(_fileSystem.GetCreationTimeUtc)
|
||||||
|
@ -76,7 +76,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var result = await organizer.OrganizeEpisodeFile(file.FullName, options, options.OverwriteExistingEpisodes, cancellationToken).ConfigureAwait(false);
|
var result = await organizer.OrganizeEpisodeFile(file.FullName, options, options.TvOptions.OverwriteExistingEpisodes, cancellationToken).ConfigureAwait(false);
|
||||||
if (result.Status == FileSortingStatus.Success && !processedFolders.Contains(file.DirectoryName, StringComparer.OrdinalIgnoreCase))
|
if (result.Status == FileSortingStatus.Success && !processedFolders.Contains(file.DirectoryName, StringComparer.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
processedFolders.Add(file.DirectoryName);
|
processedFolders.Add(file.DirectoryName);
|
||||||
|
@ -100,7 +100,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
||||||
|
|
||||||
foreach (var path in processedFolders)
|
foreach (var path in processedFolders)
|
||||||
{
|
{
|
||||||
var deleteExtensions = options.LeftOverFileExtensionsToDelete
|
var deleteExtensions = options.TvOptions.LeftOverFileExtensionsToDelete
|
||||||
.Select(i => i.Trim().TrimStart('.'))
|
.Select(i => i.Trim().TrimStart('.'))
|
||||||
.Where(i => !string.IsNullOrEmpty(i))
|
.Where(i => !string.IsNullOrEmpty(i))
|
||||||
.Select(i => "." + i)
|
.Select(i => "." + i)
|
||||||
|
@ -111,7 +111,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
||||||
DeleteLeftOverFiles(path, deleteExtensions);
|
DeleteLeftOverFiles(path, deleteExtensions);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.DeleteEmptyFolders)
|
if (options.TvOptions.DeleteEmptyFolders)
|
||||||
{
|
{
|
||||||
if (!IsWatchFolder(path, watchLocations))
|
if (!IsWatchFolder(path, watchLocations))
|
||||||
{
|
{
|
||||||
|
|
|
@ -101,6 +101,12 @@
|
||||||
<Content Include="dashboard-ui\components\chromecasthelpers.js">
|
<Content Include="dashboard-ui\components\chromecasthelpers.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
<Content Include="dashboard-ui\autoorganizesmart.html">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="dashboard-ui\bower_components\fastclick\lib\fastclick.js">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
<Content Include="dashboard-ui\components\recordingcreator\recordingcreator.js">
|
<Content Include="dashboard-ui\components\recordingcreator\recordingcreator.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
@ -281,6 +287,9 @@
|
||||||
<Content Include="dashboard-ui\scripts\mysyncsettings.js">
|
<Content Include="dashboard-ui\scripts\mysyncsettings.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
<Content Include="dashboard-ui\scripts\autoorganizesmart.js">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
<Content Include="dashboard-ui\scripts\searchmenu.js">
|
<Content Include="dashboard-ui\scripts\searchmenu.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
|
Loading…
Reference in New Issue