aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Server.Implementations/Library/LibraryManager.cs')
-rw-r--r--MediaBrowser.Server.Implementations/Library/LibraryManager.cs708
1 files changed, 605 insertions, 103 deletions
diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
index ad5eac033..37a68b52b 100644
--- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
@@ -15,9 +15,13 @@ using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Naming.Audio;
+using MediaBrowser.Naming.Common;
+using MediaBrowser.Naming.IO;
+using MediaBrowser.Naming.TV;
+using MediaBrowser.Naming.Video;
using MediaBrowser.Server.Implementations.Library.Validators;
using MediaBrowser.Server.Implementations.ScheduledTasks;
-using MoreLinq;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -64,6 +68,7 @@ namespace MediaBrowser.Server.Implementations.Library
/// </summary>
/// <value>The entity resolvers enumerable.</value>
private IItemResolver[] EntityResolvers { get; set; }
+ private IMultiItemResolver[] MultiItemResolvers { get; set; }
/// <summary>
/// Gets or sets the comparers.
@@ -192,9 +197,10 @@ namespace MediaBrowser.Server.Implementations.Library
EntityResolutionIgnoreRules = rules.ToArray();
PluginFolderCreators = pluginFolders.ToArray();
EntityResolvers = resolvers.OrderBy(i => i.Priority).ToArray();
+ MultiItemResolvers = EntityResolvers.OfType<IMultiItemResolver>().ToArray();
IntroProviders = introProviders.ToArray();
Comparers = itemComparers.ToArray();
-
+
PostscanTasks = postscanTasks.OrderBy(i =>
{
var hasOrder = i as IHasOrder;
@@ -268,7 +274,7 @@ namespace MediaBrowser.Server.Implementations.Library
{
var config = ConfigurationManager.Configuration;
- var ibnPathChanged = !string.Equals(_itemsByNamePath, ConfigurationManager.ApplicationPaths.ItemsByNamePath, StringComparison.CurrentCulture);
+ var ibnPathChanged = !string.Equals(_itemsByNamePath, ConfigurationManager.ApplicationPaths.ItemsByNamePath, StringComparison.Ordinal);
if (ibnPathChanged)
{
@@ -276,7 +282,7 @@ namespace MediaBrowser.Server.Implementations.Library
}
var newSeasonZeroName = ConfigurationManager.Configuration.SeasonZeroDisplayName;
- var seasonZeroNameChanged = !string.Equals(_seasonZeroDisplayName, newSeasonZeroName, StringComparison.CurrentCulture);
+ var seasonZeroNameChanged = !string.Equals(_seasonZeroDisplayName, newSeasonZeroName, StringComparison.Ordinal);
var wizardChanged = config.IsStartupWizardCompleted != _wizardCompleted;
RecordConfigurationValues(config);
@@ -331,7 +337,7 @@ namespace MediaBrowser.Server.Implementations.Library
{
var seasons = RootFolder.RecursiveChildren
.OfType<Season>()
- .Where(i => i.IndexNumber.HasValue && i.IndexNumber.Value == 0 && !string.Equals(i.Name, newName, StringComparison.CurrentCulture))
+ .Where(i => i.IndexNumber.HasValue && i.IndexNumber.Value == 0 && !string.Equals(i.Name, newName, StringComparison.Ordinal))
.ToList();
foreach (var season in seasons)
@@ -340,7 +346,7 @@ namespace MediaBrowser.Server.Implementations.Library
try
{
- await UpdateItem(season, ItemUpdateType.MetadataDownload, cancellationToken).ConfigureAwait(false);
+ await UpdateItem(season, ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -449,10 +455,10 @@ namespace MediaBrowser.Server.Implementations.Library
{
var list = new List<string>
{
- ConfigurationManager.ApplicationPaths.GetInternalMetadataPath(item.Id)
+ item.GetInternalMetadataPath()
};
- list.AddRange(children.Select(i => ConfigurationManager.ApplicationPaths.GetInternalMetadataPath(i.Id)));
+ list.AddRange(children.Select(i => i.GetInternalMetadataPath()));
return list;
}
@@ -462,36 +468,63 @@ namespace MediaBrowser.Server.Implementations.Library
/// </summary>
/// <param name="args">The args.</param>
/// <returns>BaseItem.</returns>
- public BaseItem ResolveItem(ItemResolveArgs args)
+ private BaseItem ResolveItem(ItemResolveArgs args)
{
- var item = EntityResolvers.Select(r =>
+ var item = EntityResolvers.Select(r => Resolve(args, r))
+ .FirstOrDefault(i => i != null);
+
+ if (item != null)
{
- try
- {
- return r.ResolvePath(args);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in {0} resolving {1}", ex, r.GetType().Name, args.Path);
+ ResolverHelper.SetInitialItemValues(item, args, _fileSystem, this);
+ }
- return null;
- }
+ return item;
+ }
- }).FirstOrDefault(i => i != null);
+ private BaseItem Resolve(ItemResolveArgs args, IItemResolver resolver)
+ {
+ try
+ {
+ return resolver.ResolvePath(args);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error in {0} resolving {1}", ex, resolver.GetType().Name, args.Path);
+ return null;
+ }
+ }
- if (item != null)
+ public Guid GetNewItemId(string key, Type type)
+ {
+ if (string.IsNullOrWhiteSpace(key))
+ {
+ throw new ArgumentNullException("key");
+ }
+ if (type == null)
{
- ResolverHelper.SetInitialItemValues(item, args, _fileSystem);
+ throw new ArgumentNullException("type");
}
- return item;
+ if (ConfigurationManager.Configuration.EnableLocalizedGuids && key.StartsWith(ConfigurationManager.ApplicationPaths.ProgramDataPath))
+ {
+ // Try to normalize paths located underneath program-data in an attempt to make them more portable
+ key = key.Substring(ConfigurationManager.ApplicationPaths.ProgramDataPath.Length)
+ .TrimStart(new[] { '/', '\\' })
+ .Replace("/", "\\");
+ }
+
+ key = type.FullName + key.ToLower();
+
+ return key.GetMD5();
}
public IEnumerable<BaseItem> ReplaceVideosWithPrimaryVersions(IEnumerable<BaseItem> items)
{
- return items.Select(i =>
+ var dict = new Dictionary<Guid, BaseItem>();
+
+ foreach (var item in items)
{
- var video = i as Video;
+ var video = item as Video;
if (video != null)
{
@@ -501,14 +534,15 @@ namespace MediaBrowser.Server.Implementations.Library
if (primary != null)
{
- return primary;
+ dict[primary.Id] = primary;
+ continue;
}
}
}
+ dict[item.Id] = item;
+ }
- return i;
-
- }).DistinctBy(i => i.Id);
+ return dict.Values;
}
/// <summary>
@@ -527,31 +561,32 @@ namespace MediaBrowser.Server.Implementations.Library
return item;
}
- public BaseItem ResolvePath(FileSystemInfo fileInfo, Folder parent = null)
+ public BaseItem ResolvePath(FileSystemInfo fileInfo,
+ Folder parent = null)
{
return ResolvePath(fileInfo, new DirectoryService(_logger), parent);
}
- /// <summary>
- /// Resolves a path into a BaseItem
- /// </summary>
- /// <param name="fileInfo">The file info.</param>
- /// <param name="directoryService">The directory service.</param>
- /// <param name="parent">The parent.</param>
- /// <returns>BaseItem.</returns>
- /// <exception cref="System.ArgumentNullException">fileInfo</exception>
- public BaseItem ResolvePath(FileSystemInfo fileInfo, IDirectoryService directoryService, Folder parent = null)
+ private BaseItem ResolvePath(FileSystemInfo fileInfo, IDirectoryService directoryService, Folder parent = null, string collectionType = null)
{
if (fileInfo == null)
{
throw new ArgumentNullException("fileInfo");
}
+ var fullPath = fileInfo.FullName;
+
+ if (string.IsNullOrWhiteSpace(collectionType))
+ {
+ collectionType = GetConfiguredContentType(fullPath);
+ }
+
var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, this, directoryService)
{
Parent = parent,
- Path = fileInfo.FullName,
- FileInfo = fileInfo
+ Path = fullPath,
+ FileInfo = fileInfo,
+ CollectionType = collectionType
};
// Return null if ignore rules deem that we should do so
@@ -619,40 +654,48 @@ namespace MediaBrowser.Server.Implementations.Library
return !args.ContainsFileSystemEntryByName(".ignore");
}
- /// <summary>
- /// Resolves a set of files into a list of BaseItem
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="files">The files.</param>
- /// <param name="directoryService">The directory service.</param>
- /// <param name="parent">The parent.</param>
- /// <returns>List{``0}.</returns>
- public List<T> ResolvePaths<T>(IEnumerable<FileSystemInfo> files, IDirectoryService directoryService, Folder parent)
- where T : BaseItem
+ public IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemInfo> files, IDirectoryService directoryService, Folder parent, string collectionType)
{
- var list = new List<T>();
+ var fileList = files.ToList();
- Parallel.ForEach(files, f =>
+ if (parent != null)
{
- try
+ foreach (var resolver in MultiItemResolvers)
{
- var item = ResolvePath(f, directoryService, parent) as T;
+ var result = resolver.ResolveMultiple(parent, fileList, collectionType, directoryService);
- if (item != null)
+ if (result != null && result.Items.Count > 0)
{
- lock (list)
+ var items = new List<BaseItem>();
+ items.AddRange(result.Items);
+
+ foreach (var item in items)
{
- list.Add(item);
+ ResolverHelper.SetInitialItemValues(item, parent, _fileSystem, this, directoryService);
}
+ items.AddRange(ResolveFileList(result.ExtraFiles, directoryService, parent, collectionType));
+ return items;
}
}
+ }
+
+ return ResolveFileList(fileList, directoryService, parent, collectionType);
+ }
+
+ private IEnumerable<BaseItem> ResolveFileList(IEnumerable<FileSystemInfo> fileList, IDirectoryService directoryService, Folder parent, string collectionType)
+ {
+ return fileList.Select(f =>
+ {
+ try
+ {
+ return ResolvePath(f, directoryService, parent, collectionType);
+ }
catch (Exception ex)
{
_logger.ErrorException("Error resolving path {0}", ex, f.FullName);
+ return null;
}
- });
-
- return list;
+ }).Where(i => i != null);
}
/// <summary>
@@ -666,7 +709,7 @@ namespace MediaBrowser.Server.Implementations.Library
Directory.CreateDirectory(rootFolderPath);
- var rootFolder = GetItemById(rootFolderPath.GetMBId(typeof(AggregateFolder))) as AggregateFolder ?? (AggregateFolder)ResolvePath(new DirectoryInfo(rootFolderPath));
+ var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ?? (AggregateFolder)ResolvePath(new DirectoryInfo(rootFolderPath));
// Add in the plug-in folders
foreach (var child in PluginFolderCreators)
@@ -677,7 +720,14 @@ namespace MediaBrowser.Server.Implementations.Library
{
if (folder.Id == Guid.Empty)
{
- folder.Id = (folder.Path ?? folder.GetType().Name).GetMBId(folder.GetType());
+ if (string.IsNullOrWhiteSpace(folder.Path))
+ {
+ folder.Id = GetNewItemId(folder.GetType().Name, folder.GetType());
+ }
+ else
+ {
+ folder.Id = GetNewItemId(folder.Path, folder.GetType());
+ }
}
folder = GetItemById(folder.Id) as BasePluginFolder ?? folder;
@@ -692,16 +742,27 @@ namespace MediaBrowser.Server.Implementations.Library
}
private UserRootFolder _userRootFolder;
+ private readonly object _syncLock = new object();
public Folder GetUserRootFolder()
{
if (_userRootFolder == null)
{
- var userRootPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
+ lock (_syncLock)
+ {
+ if (_userRootFolder == null)
+ {
+ var userRootPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
- Directory.CreateDirectory(userRootPath);
+ Directory.CreateDirectory(userRootPath);
- _userRootFolder = GetItemById(userRootPath.GetMBId(typeof(UserRootFolder))) as UserRootFolder ??
- (UserRootFolder)ResolvePath(new DirectoryInfo(userRootPath));
+ _userRootFolder = GetItemById(GetNewItemId(userRootPath, typeof(UserRootFolder))) as UserRootFolder;
+
+ if (_userRootFolder == null)
+ {
+ _userRootFolder = (UserRootFolder)ResolvePath(new DirectoryInfo(userRootPath));
+ }
+ }
+ }
}
return _userRootFolder;
@@ -807,7 +868,7 @@ namespace MediaBrowser.Server.Implementations.Library
var type = typeof(T);
- if (type == typeof(Person) && ConfigurationManager.Configuration.EnablePeoplePrefixSubFolders)
+ if (type == typeof(Person))
{
subFolderPrefix = validFilename.Substring(0, 1);
}
@@ -816,7 +877,7 @@ namespace MediaBrowser.Server.Implementations.Library
Path.Combine(path, validFilename) :
Path.Combine(path, subFolderPrefix, validFilename);
- var id = fullPath.GetMBId(type);
+ var id = GetNewItemId(fullPath, type);
BaseItem obj;
@@ -1187,26 +1248,81 @@ namespace MediaBrowser.Server.Implementations.Library
return item;
}
+ public BaseItem GetMemoryItemById(Guid id)
+ {
+ if (id == Guid.Empty)
+ {
+ throw new ArgumentNullException("id");
+ }
+
+ BaseItem item;
+
+ LibraryItemsCache.TryGetValue(id, out item);
+
+ return item;
+ }
+
/// <summary>
/// Gets the intros.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="user">The user.</param>
/// <returns>IEnumerable{System.String}.</returns>
- public IEnumerable<Video> GetIntros(BaseItem item, User user)
+ public async Task<IEnumerable<Video>> GetIntros(BaseItem item, User user)
{
- return IntroProviders.SelectMany(i => i.GetIntros(item, user))
+ var tasks = IntroProviders
+ .OrderBy(i => (i.GetType().Name.IndexOf("Default", StringComparison.OrdinalIgnoreCase) == -1 ? 0 : 1))
+ .Take(1)
+ .Select(i => GetIntros(i, item, user));
+
+ var items = await Task.WhenAll(tasks).ConfigureAwait(false);
+
+ return items
+ .SelectMany(i => i.ToArray())
.Select(ResolveIntro)
.Where(i => i != null);
}
/// <summary>
+ /// Gets the intros.
+ /// </summary>
+ /// <param name="provider">The provider.</param>
+ /// <param name="item">The item.</param>
+ /// <param name="user">The user.</param>
+ /// <returns>Task&lt;IEnumerable&lt;IntroInfo&gt;&gt;.</returns>
+ private async Task<IEnumerable<IntroInfo>> GetIntros(IIntroProvider provider, BaseItem item, User user)
+ {
+ try
+ {
+ return await provider.GetIntros(item, user).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error getting intros", ex);
+
+ return new List<IntroInfo>();
+ }
+ }
+
+ /// <summary>
/// Gets all intro files.
/// </summary>
/// <returns>IEnumerable{System.String}.</returns>
public IEnumerable<string> GetAllIntroFiles()
{
- return IntroProviders.SelectMany(i => i.GetAllIntroFiles());
+ return IntroProviders.SelectMany(i =>
+ {
+ try
+ {
+ return i.GetAllIntroFiles().ToList();
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error getting intro files", ex);
+
+ return new List<string>();
+ }
+ });
}
/// <summary>
@@ -1383,7 +1499,8 @@ namespace MediaBrowser.Server.Implementations.Library
item.DateLastSaved = DateTime.UtcNow;
- _logger.Debug("Saving {0} to database.", item.Path ?? item.Name);
+ var logName = item.LocationType == LocationType.Remote ? item.Name ?? item.Path : item.Path ?? item.Name;
+ _logger.Debug("Saving {0} to database.", logName);
await ItemRepository.SaveItem(item, cancellationToken).ConfigureAwait(false);
@@ -1435,12 +1552,48 @@ namespace MediaBrowser.Server.Implementations.Library
return ItemRepository.RetrieveItem(id);
}
- /// <summary>
- /// Finds the type of the collection.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>System.String.</returns>
- public string FindCollectionType(BaseItem item)
+ public string GetContentType(BaseItem item)
+ {
+ // Types cannot be overridden, so go from the top down until we find a configured content type
+
+ var type = GetInheritedContentType(item);
+
+ if (!string.IsNullOrWhiteSpace(type))
+ {
+ return type;
+ }
+
+ return GetConfiguredContentType(item);
+ }
+
+ public string GetInheritedContentType(BaseItem item)
+ {
+ var type = GetTopFolderContentType(item);
+
+ if (!string.IsNullOrWhiteSpace(type))
+ {
+ return type;
+ }
+
+ return item.Parents
+ .Select(GetConfiguredContentType)
+ .LastOrDefault(i => !string.IsNullOrWhiteSpace(i));
+ }
+
+ private string GetConfiguredContentType(BaseItem item)
+ {
+ return GetConfiguredContentType(item.ContainingFolderPath);
+ }
+
+ private string GetConfiguredContentType(string path)
+ {
+ var type = ConfigurationManager.Configuration.ContentTypes
+ .FirstOrDefault(i => string.Equals(i.Name, path, StringComparison.OrdinalIgnoreCase) || _fileSystem.ContainsSubPath(i.Name, path));
+
+ return type == null ? null : type.Value;
+ }
+
+ private string GetTopFolderContentType(BaseItem item)
{
while (!(item.Parent is AggregateFolder) && item.Parent != null)
{
@@ -1452,42 +1605,30 @@ namespace MediaBrowser.Server.Implementations.Library
return null;
}
- var collectionTypes = _userManager.Users
- .Select(i => i.RootFolder)
- .Distinct()
- .SelectMany(i => i.Children)
- .OfType<CollectionFolder>()
+ return GetUserRootFolder().Children
+ .OfType<ICollectionFolder>()
.Where(i => string.Equals(i.Path, item.Path, StringComparison.OrdinalIgnoreCase) || i.PhysicalLocations.Contains(item.Path))
.Select(i => i.CollectionType)
- .Where(i => !string.IsNullOrEmpty(i))
- .Distinct()
- .ToList();
-
- return collectionTypes.Count == 1 ? collectionTypes[0] : null;
+ .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
}
- public Task<UserView> GetNamedView(string name, string type, string sortName, CancellationToken cancellationToken)
- {
- return GetNamedView(name, null, type, sortName, cancellationToken);
- }
-
- public async Task<UserView> GetNamedView(string name, string category, string type, string sortName, CancellationToken cancellationToken)
+ public async Task<UserView> GetNamedView(string name,
+ string type,
+ string sortName,
+ CancellationToken cancellationToken)
{
var path = Path.Combine(ConfigurationManager.ApplicationPaths.ItemsByNamePath,
"views");
- if (!string.IsNullOrWhiteSpace(category))
- {
- path = Path.Combine(path, _fileSystem.GetValidFilename(category));
- }
-
path = Path.Combine(path, _fileSystem.GetValidFilename(type));
- var id = (path + "_namedview_" + name).GetMBId(typeof(UserView));
+ var id = GetNewItemId(path + "_namedview_" + name, typeof(UserView));
var item = GetItemById(id) as UserView;
- if (item == null ||
+ var refresh = false;
+
+ if (item == null ||
!string.Equals(item.Path, path, StringComparison.OrdinalIgnoreCase))
{
Directory.CreateDirectory(path);
@@ -1504,10 +1645,371 @@ namespace MediaBrowser.Server.Implementations.Library
await CreateItem(item, cancellationToken).ConfigureAwait(false);
- await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
+ refresh = true;
+ }
+
+ if (!refresh && item != null)
+ {
+ refresh = (DateTime.UtcNow - item.DateLastSaved).TotalHours >= 24;
+ }
+
+ if (refresh)
+ {
+ await item.RefreshMetadata(new MetadataRefreshOptions
+ {
+ ForceSave = true
+
+ }, cancellationToken).ConfigureAwait(false);
}
return item;
}
+
+ public async Task<UserView> GetSpecialFolder(User user,
+ string name,
+ string parentId,
+ string viewType,
+ string sortName,
+ CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ {
+ throw new ArgumentNullException("name");
+ }
+
+ if (string.IsNullOrWhiteSpace(parentId))
+ {
+ throw new ArgumentNullException("parentId");
+ }
+
+ if (string.IsNullOrWhiteSpace(viewType))
+ {
+ throw new ArgumentNullException("viewType");
+ }
+
+ var id = GetNewItemId("7_namedview_" + name + user.Id.ToString("N") + parentId, typeof(UserView));
+
+ var path = BaseItem.GetInternalMetadataPathForId(id);
+
+ var item = GetItemById(id) as UserView;
+
+ var refresh = false;
+
+ if (item == null)
+ {
+ Directory.CreateDirectory(path);
+
+ item = new UserView
+ {
+ Path = path,
+ Id = id,
+ DateCreated = DateTime.UtcNow,
+ Name = name,
+ ViewType = viewType,
+ ForcedSortName = sortName,
+ UserId = user.Id,
+ ParentId = new Guid(parentId)
+ };
+
+ await CreateItem(item, cancellationToken).ConfigureAwait(false);
+
+ refresh = true;
+ }
+
+ if (!refresh && item != null)
+ {
+ refresh = (DateTime.UtcNow - item.DateLastSaved).TotalHours >= 24;
+ }
+
+ if (refresh)
+ {
+ await item.RefreshMetadata(new MetadataRefreshOptions
+ {
+ ForceSave = true
+
+ }, cancellationToken).ConfigureAwait(false);
+ }
+
+ return item;
+ }
+
+ public bool IsVideoFile(string path)
+ {
+ var resolver = new VideoResolver(new ExtendedNamingOptions(), new Naming.Logging.NullLogger());
+ return resolver.IsVideoFile(path);
+ }
+
+ public bool IsAudioFile(string path)
+ {
+ var parser = new AudioFileParser(new ExtendedNamingOptions());
+ return parser.IsAudioFile(path);
+ }
+
+ public int? GetSeasonNumberFromPath(string path)
+ {
+ return new SeasonPathParser(new ExtendedNamingOptions(), new RegexProvider()).Parse(path, true).SeasonNumber;
+ }
+
+ public bool FillMissingEpisodeNumbersFromPath(Episode episode)
+ {
+ var resolver = new EpisodeResolver(new ExtendedNamingOptions(),
+ new Naming.Logging.NullLogger());
+
+ var fileType = episode.VideoType == VideoType.BluRay || episode.VideoType == VideoType.Dvd || episode.VideoType == VideoType.HdDvd ?
+ FileInfoType.Directory :
+ FileInfoType.File;
+
+ var locationType = episode.LocationType;
+
+ var episodeInfo = locationType == LocationType.FileSystem || locationType == LocationType.Offline ?
+ resolver.Resolve(episode.Path, fileType) :
+ new Naming.TV.EpisodeInfo();
+
+ if (episodeInfo == null)
+ {
+ episodeInfo = new Naming.TV.EpisodeInfo();
+ }
+
+ var changed = false;
+
+ if (episodeInfo.IsByDate)
+ {
+ if (episode.IndexNumber.HasValue)
+ {
+ episode.IndexNumber = null;
+ changed = true;
+ }
+
+ if (episode.IndexNumberEnd.HasValue)
+ {
+ episode.IndexNumberEnd = null;
+ changed = true;
+ }
+
+ if (!episode.PremiereDate.HasValue)
+ {
+ if (episodeInfo.Year.HasValue && episodeInfo.Month.HasValue && episodeInfo.Day.HasValue)
+ {
+ episode.PremiereDate = new DateTime(episodeInfo.Year.Value, episodeInfo.Month.Value, episodeInfo.Day.Value).ToUniversalTime();
+ }
+
+ if (episode.PremiereDate.HasValue)
+ {
+ changed = true;
+ }
+ }
+
+ if (!episode.ProductionYear.HasValue)
+ {
+ episode.ProductionYear = episodeInfo.Year;
+
+ if (episode.ProductionYear.HasValue)
+ {
+ changed = true;
+ }
+ }
+
+ if (!episode.ParentIndexNumber.HasValue)
+ {
+ var season = episode.Season;
+
+ if (season != null)
+ {
+ episode.ParentIndexNumber = season.IndexNumber;
+ }
+
+ if (episode.ParentIndexNumber.HasValue)
+ {
+ changed = true;
+ }
+ }
+ }
+ else
+ {
+ if (!episode.IndexNumber.HasValue)
+ {
+ episode.IndexNumber = episodeInfo.EpisodeNumber;
+
+ if (episode.IndexNumber.HasValue)
+ {
+ changed = true;
+ }
+ }
+
+ if (!episode.IndexNumberEnd.HasValue)
+ {
+ episode.IndexNumberEnd = episodeInfo.EndingEpsiodeNumber;
+
+ if (episode.IndexNumberEnd.HasValue)
+ {
+ changed = true;
+ }
+ }
+
+ if (!episode.ParentIndexNumber.HasValue)
+ {
+ episode.ParentIndexNumber = episodeInfo.SeasonNumber;
+
+ if (!episode.ParentIndexNumber.HasValue)
+ {
+ var season = episode.Season;
+
+ if (season != null)
+ {
+ episode.ParentIndexNumber = season.IndexNumber;
+ }
+ }
+
+ if (episode.ParentIndexNumber.HasValue)
+ {
+ changed = true;
+ }
+ }
+ }
+
+ return changed;
+ }
+
+ public ItemLookupInfo ParseName(string name)
+ {
+ var resolver = new VideoResolver(new ExtendedNamingOptions(), new Naming.Logging.NullLogger());
+
+ var result = resolver.CleanDateTime(name);
+ var cleanName = resolver.CleanString(result.Name);
+
+ return new ItemLookupInfo
+ {
+ Name = cleanName.Name,
+ Year = result.Year
+ };
+ }
+
+ public IEnumerable<Video> FindTrailers(BaseItem owner, List<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService)
+ {
+ var files = fileSystemChildren.OfType<DirectoryInfo>()
+ .Where(i => string.Equals(i.Name, BaseItem.TrailerFolderName, StringComparison.OrdinalIgnoreCase))
+ .SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly))
+ .ToList();
+
+ var videoListResolver = new VideoListResolver(new ExtendedNamingOptions(), new Naming.Logging.NullLogger());
+
+ var videos = videoListResolver.Resolve(fileSystemChildren.Select(i => new PortableFileInfo
+ {
+ FullName = i.FullName,
+ Type = GetFileType(i)
+
+ }).ToList());
+
+ var currentVideo = videos.FirstOrDefault(i => string.Equals(owner.Path, i.Files.First().Path, StringComparison.OrdinalIgnoreCase));
+
+ if (currentVideo != null)
+ {
+ files.AddRange(currentVideo.Extras.Where(i => string.Equals(i.ExtraType, "trailer", StringComparison.OrdinalIgnoreCase)).Select(i => new FileInfo(i.Path)));
+ }
+
+ return ResolvePaths(files, directoryService, null, null)
+ .OfType<Video>()
+ .Select(video =>
+ {
+ // Try to retrieve it from the db. If we don't find it, use the resolved version
+ var dbItem = GetItemById(video.Id) as Video;
+
+ if (dbItem != null)
+ {
+ video = dbItem;
+ }
+
+ video.ExtraType = ExtraType.Trailer;
+
+ return video;
+
+ // Sort them so that the list can be easily compared for changes
+ }).OrderBy(i => i.Path).ToList();
+ }
+
+ private FileInfoType GetFileType(FileSystemInfo info)
+ {
+ if ((info.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
+ {
+ return FileInfoType.Directory;
+ }
+
+ return FileInfoType.File;
+ }
+
+ public IEnumerable<Video> FindExtras(BaseItem owner, List<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService)
+ {
+ var files = fileSystemChildren.OfType<DirectoryInfo>()
+ .Where(i => string.Equals(i.Name, "extras", StringComparison.OrdinalIgnoreCase) || string.Equals(i.Name, "specials", StringComparison.OrdinalIgnoreCase))
+ .SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly))
+ .ToList();
+
+ var videoListResolver = new VideoListResolver(new ExtendedNamingOptions(), new Naming.Logging.NullLogger());
+
+ var videos = videoListResolver.Resolve(fileSystemChildren.Select(i => new PortableFileInfo
+ {
+ FullName = i.FullName,
+ Type = GetFileType(i)
+
+ }).ToList());
+
+ var currentVideo = videos.FirstOrDefault(i => string.Equals(owner.Path, i.Files.First().Path, StringComparison.OrdinalIgnoreCase));
+
+ if (currentVideo != null)
+ {
+ files.AddRange(currentVideo.Extras.Where(i => !string.Equals(i.ExtraType, "trailer", StringComparison.OrdinalIgnoreCase)).Select(i => new FileInfo(i.Path)));
+ }
+
+ return ResolvePaths(files, directoryService, null, null)
+ .OfType<Video>()
+ .Select(video =>
+ {
+ // Try to retrieve it from the db. If we don't find it, use the resolved version
+ var dbItem = GetItemById(video.Id) as Video;
+
+ if (dbItem != null)
+ {
+ video = dbItem;
+ }
+
+ SetExtraTypeFromFilename(video);
+
+ return video;
+
+ // Sort them so that the list can be easily compared for changes
+ }).OrderBy(i => i.Path).ToList();
+ }
+
+ private void SetExtraTypeFromFilename(Video item)
+ {
+ var resolver = new ExtraResolver(new ExtendedNamingOptions(), new Naming.Logging.NullLogger(), new RegexProvider());
+
+ var result = resolver.GetExtraInfo(item.Path);
+
+ if (string.Equals(result.ExtraType, "deletedscene", StringComparison.OrdinalIgnoreCase))
+ {
+ item.ExtraType = ExtraType.DeletedScene;
+ }
+ else if (string.Equals(result.ExtraType, "behindthescenes", StringComparison.OrdinalIgnoreCase))
+ {
+ item.ExtraType = ExtraType.BehindTheScenes;
+ }
+ else if (string.Equals(result.ExtraType, "interview", StringComparison.OrdinalIgnoreCase))
+ {
+ item.ExtraType = ExtraType.Interview;
+ }
+ else if (string.Equals(result.ExtraType, "scene", StringComparison.OrdinalIgnoreCase))
+ {
+ item.ExtraType = ExtraType.Scene;
+ }
+ else if (string.Equals(result.ExtraType, "sample", StringComparison.OrdinalIgnoreCase))
+ {
+ item.ExtraType = ExtraType.Sample;
+ }
+ else
+ {
+ item.ExtraType = ExtraType.Clip;
+ }
+ }
}
}