diff options
Diffstat (limited to 'MediaBrowser.Server.Implementations')
83 files changed, 39 insertions, 13312 deletions
diff --git a/MediaBrowser.Server.Implementations/Activity/ActivityManager.cs b/MediaBrowser.Server.Implementations/Activity/ActivityManager.cs deleted file mode 100644 index 2af9395c3..000000000 --- a/MediaBrowser.Server.Implementations/Activity/ActivityManager.cs +++ /dev/null @@ -1,56 +0,0 @@ -using MediaBrowser.Common.Events; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Activity; -using MediaBrowser.Model.Events; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Querying; -using System; -using System.Linq; -using System.Threading.Tasks; - -namespace MediaBrowser.Server.Implementations.Activity -{ - public class ActivityManager : IActivityManager - { - public event EventHandler<GenericEventArgs<ActivityLogEntry>> EntryCreated; - - private readonly IActivityRepository _repo; - private readonly ILogger _logger; - private readonly IUserManager _userManager; - - public ActivityManager(ILogger logger, IActivityRepository repo, IUserManager userManager) - { - _logger = logger; - _repo = repo; - _userManager = userManager; - } - - public async Task Create(ActivityLogEntry entry) - { - entry.Id = Guid.NewGuid().ToString("N"); - entry.Date = DateTime.UtcNow; - - await _repo.Create(entry).ConfigureAwait(false); - - EventHelper.FireEventIfNotNull(EntryCreated, this, new GenericEventArgs<ActivityLogEntry>(entry), _logger); - } - - public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit) - { - var result = _repo.GetActivityLogEntries(minDate, startIndex, limit); - - foreach (var item in result.Items.Where(i => !string.IsNullOrWhiteSpace(i.UserId))) - { - var user = _userManager.GetUserById(item.UserId); - - if (user != null) - { - var dto = _userManager.GetUserDto(user); - item.UserPrimaryImageTag = dto.PrimaryImageTag; - } - } - - return result; - } - } -} diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs deleted file mode 100644 index a06656b21..000000000 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ /dev/null @@ -1,1609 +0,0 @@ -using MediaBrowser.Common; -using MediaBrowser.Controller.Channels; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Devices; -using MediaBrowser.Controller.Drawing; -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Controller.Persistence; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Controller.Sync; -using MediaBrowser.Model.Drawing; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Querying; -using MediaBrowser.Model.Sync; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.IO; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Extensions; - -namespace MediaBrowser.Server.Implementations.Dto -{ - public class DtoService : IDtoService - { - private readonly ILogger _logger; - private readonly ILibraryManager _libraryManager; - private readonly IUserDataManager _userDataRepository; - private readonly IItemRepository _itemRepo; - - private readonly IImageProcessor _imageProcessor; - private readonly IServerConfigurationManager _config; - private readonly IFileSystem _fileSystem; - private readonly IProviderManager _providerManager; - - private readonly Func<IChannelManager> _channelManagerFactory; - private readonly ISyncManager _syncManager; - private readonly IApplicationHost _appHost; - private readonly Func<IDeviceManager> _deviceManager; - private readonly Func<IMediaSourceManager> _mediaSourceManager; - private readonly Func<ILiveTvManager> _livetvManager; - - public DtoService(ILogger logger, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepo, IImageProcessor imageProcessor, IServerConfigurationManager config, IFileSystem fileSystem, IProviderManager providerManager, Func<IChannelManager> channelManagerFactory, ISyncManager syncManager, IApplicationHost appHost, Func<IDeviceManager> deviceManager, Func<IMediaSourceManager> mediaSourceManager, Func<ILiveTvManager> livetvManager) - { - _logger = logger; - _libraryManager = libraryManager; - _userDataRepository = userDataRepository; - _itemRepo = itemRepo; - _imageProcessor = imageProcessor; - _config = config; - _fileSystem = fileSystem; - _providerManager = providerManager; - _channelManagerFactory = channelManagerFactory; - _syncManager = syncManager; - _appHost = appHost; - _deviceManager = deviceManager; - _mediaSourceManager = mediaSourceManager; - _livetvManager = livetvManager; - } - - /// <summary> - /// Converts a BaseItem to a DTOBaseItem - /// </summary> - /// <param name="item">The item.</param> - /// <param name="fields">The fields.</param> - /// <param name="user">The user.</param> - /// <param name="owner">The owner.</param> - /// <returns>Task{DtoBaseItem}.</returns> - /// <exception cref="System.ArgumentNullException">item</exception> - public BaseItemDto GetBaseItemDto(BaseItem item, List<ItemFields> fields, User user = null, BaseItem owner = null) - { - var options = new DtoOptions - { - Fields = fields - }; - - return GetBaseItemDto(item, options, user, owner); - } - - public async Task<List<BaseItemDto>> GetBaseItemDtos(IEnumerable<BaseItem> items, DtoOptions options, User user = null, BaseItem owner = null) - { - if (items == null) - { - throw new ArgumentNullException("items"); - } - - if (options == null) - { - throw new ArgumentNullException("options"); - } - - var syncDictionary = GetSyncedItemProgress(options); - - var list = new List<BaseItemDto>(); - var programTuples = new List<Tuple<BaseItem, BaseItemDto>>(); - var channelTuples = new List<Tuple<BaseItemDto, LiveTvChannel>>(); - - foreach (var item in items) - { - var dto = await GetBaseItemDtoInternal(item, options, user, owner).ConfigureAwait(false); - - var tvChannel = item as LiveTvChannel; - if (tvChannel != null) - { - channelTuples.Add(new Tuple<BaseItemDto, LiveTvChannel>(dto, tvChannel)); - } - else if (item is LiveTvProgram) - { - programTuples.Add(new Tuple<BaseItem, BaseItemDto>(item, dto)); - } - - var byName = item as IItemByName; - - if (byName != null) - { - if (options.Fields.Contains(ItemFields.ItemCounts)) - { - var libraryItems = byName.GetTaggedItems(new InternalItemsQuery(user) - { - Recursive = true - }); - - SetItemByNameInfo(item, dto, libraryItems.ToList(), user); - } - } - - FillSyncInfo(dto, item, options, user, syncDictionary); - - list.Add(dto); - } - - if (programTuples.Count > 0) - { - await _livetvManager().AddInfoToProgramDto(programTuples, options.Fields, user).ConfigureAwait(false); - } - - if (channelTuples.Count > 0) - { - _livetvManager().AddChannelInfo(channelTuples, options, user); - } - - return list; - } - - public BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null) - { - var syncDictionary = GetSyncedItemProgress(options); - - var dto = GetBaseItemDtoInternal(item, options, user, owner).Result; - var tvChannel = item as LiveTvChannel; - if (tvChannel != null) - { - var list = new List<Tuple<BaseItemDto, LiveTvChannel>> { new Tuple<BaseItemDto, LiveTvChannel>(dto, tvChannel) }; - _livetvManager().AddChannelInfo(list, options, user); - } - else if (item is LiveTvProgram) - { - var list = new List<Tuple<BaseItem, BaseItemDto>> { new Tuple<BaseItem, BaseItemDto>(item, dto) }; - var task = _livetvManager().AddInfoToProgramDto(list, options.Fields, user); - Task.WaitAll(task); - } - - var byName = item as IItemByName; - - if (byName != null) - { - if (options.Fields.Contains(ItemFields.ItemCounts)) - { - SetItemByNameInfo(item, dto, GetTaggedItems(byName, user), user); - } - - FillSyncInfo(dto, item, options, user, syncDictionary); - return dto; - } - - FillSyncInfo(dto, item, options, user, syncDictionary); - - return dto; - } - - private List<BaseItem> GetTaggedItems(IItemByName byName, User user) - { - var items = byName.GetTaggedItems(new InternalItemsQuery(user) - { - Recursive = true - - }).ToList(); - - return items; - } - - public Dictionary<string, SyncedItemProgress> GetSyncedItemProgress(DtoOptions options) - { - if (!options.Fields.Contains(ItemFields.BasicSyncInfo) && - !options.Fields.Contains(ItemFields.SyncInfo)) - { - return new Dictionary<string, SyncedItemProgress>(); - } - - var deviceId = options.DeviceId; - if (string.IsNullOrWhiteSpace(deviceId)) - { - return new Dictionary<string, SyncedItemProgress>(); - } - - var caps = _deviceManager().GetCapabilities(deviceId); - if (caps == null || !caps.SupportsSync) - { - return new Dictionary<string, SyncedItemProgress>(); - } - - return _syncManager.GetSyncedItemProgresses(new SyncJobItemQuery - { - TargetId = deviceId, - Statuses = new[] - { - SyncJobItemStatus.Converting, - SyncJobItemStatus.Queued, - SyncJobItemStatus.Transferring, - SyncJobItemStatus.ReadyToTransfer, - SyncJobItemStatus.Synced - } - }); - } - - public void FillSyncInfo(IEnumerable<Tuple<BaseItem, BaseItemDto>> tuples, DtoOptions options, User user) - { - if (options.Fields.Contains(ItemFields.BasicSyncInfo) || - options.Fields.Contains(ItemFields.SyncInfo)) - { - var syncProgress = GetSyncedItemProgress(options); - - foreach (var tuple in tuples) - { - var item = tuple.Item1; - - FillSyncInfo(tuple.Item2, item, options, user, syncProgress); - } - } - } - - private void FillSyncInfo(IHasSyncInfo dto, BaseItem item, DtoOptions options, User user, Dictionary<string, SyncedItemProgress> syncProgress) - { - var hasFullSyncInfo = options.Fields.Contains(ItemFields.SyncInfo); - - if (!options.Fields.Contains(ItemFields.BasicSyncInfo) && - !hasFullSyncInfo) - { - return; - } - - if (dto.SupportsSync ?? false) - { - SyncedItemProgress syncStatus; - if (syncProgress.TryGetValue(dto.Id, out syncStatus)) - { - if (syncStatus.Status == SyncJobItemStatus.Synced) - { - dto.SyncPercent = 100; - } - else - { - dto.SyncPercent = syncStatus.Progress; - } - - if (hasFullSyncInfo) - { - dto.HasSyncJob = true; - dto.SyncStatus = syncStatus.Status; - } - } - } - } - - private async Task<BaseItemDto> GetBaseItemDtoInternal(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null) - { - var fields = options.Fields; - - if (item == null) - { - throw new ArgumentNullException("item"); - } - - if (fields == null) - { - throw new ArgumentNullException("fields"); - } - - var dto = new BaseItemDto - { - ServerId = _appHost.SystemId - }; - - if (item.SourceType == SourceType.Channel) - { - dto.SourceType = item.SourceType.ToString(); - } - - if (fields.Contains(ItemFields.People)) - { - AttachPeople(dto, item); - } - - if (fields.Contains(ItemFields.PrimaryImageAspectRatio)) - { - try - { - AttachPrimaryImageAspectRatio(dto, item); - } - catch (Exception ex) - { - // Have to use a catch-all unfortunately because some .net image methods throw plain Exceptions - _logger.ErrorException("Error generating PrimaryImageAspectRatio for {0}", ex, item.Name); - } - } - - if (fields.Contains(ItemFields.DisplayPreferencesId)) - { - dto.DisplayPreferencesId = item.DisplayPreferencesId.ToString("N"); - } - - if (user != null) - { - await AttachUserSpecificInfo(dto, item, user, options).ConfigureAwait(false); - } - - var hasMediaSources = item as IHasMediaSources; - if (hasMediaSources != null) - { - if (fields.Contains(ItemFields.MediaSources)) - { - if (user == null) - { - dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(hasMediaSources, true).ToList(); - } - else - { - dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(hasMediaSources, true, user).ToList(); - } - } - } - - if (fields.Contains(ItemFields.Studios)) - { - AttachStudios(dto, item); - } - - AttachBasicFields(dto, item, owner, options); - - var collectionFolder = item as ICollectionFolder; - if (collectionFolder != null) - { - dto.OriginalCollectionType = collectionFolder.CollectionType; - - dto.CollectionType = user == null ? - collectionFolder.CollectionType : - collectionFolder.GetViewType(user); - } - - if (fields.Contains(ItemFields.CanDelete)) - { - dto.CanDelete = user == null - ? item.CanDelete() - : item.CanDelete(user); - } - - if (fields.Contains(ItemFields.CanDownload)) - { - dto.CanDownload = user == null - ? item.CanDownload() - : item.CanDownload(user); - } - - if (fields.Contains(ItemFields.Etag)) - { - dto.Etag = item.GetEtag(user); - } - - if (item is ILiveTvRecording) - { - _livetvManager().AddInfoToRecordingDto(item, dto, user); - } - - return dto; - } - - public BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List<BaseItem> taggedItems, Dictionary<string, SyncedItemProgress> syncProgress, User user = null) - { - var dto = GetBaseItemDtoInternal(item, options, user).Result; - - if (taggedItems != null && options.Fields.Contains(ItemFields.ItemCounts)) - { - SetItemByNameInfo(item, dto, taggedItems, user); - } - - FillSyncInfo(dto, item, options, user, syncProgress); - - return dto; - } - - private void SetItemByNameInfo(BaseItem item, BaseItemDto dto, List<BaseItem> taggedItems, User user = null) - { - if (item is MusicArtist) - { - dto.AlbumCount = taggedItems.Count(i => i is MusicAlbum); - dto.MusicVideoCount = taggedItems.Count(i => i is MusicVideo); - dto.SongCount = taggedItems.Count(i => i is Audio); - } - else if (item is MusicGenre) - { - dto.ArtistCount = taggedItems.Count(i => i is MusicArtist); - dto.AlbumCount = taggedItems.Count(i => i is MusicAlbum); - dto.MusicVideoCount = taggedItems.Count(i => i is MusicVideo); - dto.SongCount = taggedItems.Count(i => i is Audio); - } - else if (item is GameGenre) - { - dto.GameCount = taggedItems.Count(i => i is Game); - } - else - { - // This populates them all and covers Genre, Person, Studio, Year - - dto.ArtistCount = taggedItems.Count(i => i is MusicArtist); - dto.AlbumCount = taggedItems.Count(i => i is MusicAlbum); - dto.EpisodeCount = taggedItems.Count(i => i is Episode); - dto.GameCount = taggedItems.Count(i => i is Game); - dto.MovieCount = taggedItems.Count(i => i is Movie); - dto.TrailerCount = taggedItems.Count(i => i is Trailer); - dto.MusicVideoCount = taggedItems.Count(i => i is MusicVideo); - dto.SeriesCount = taggedItems.Count(i => i is Series); - dto.ProgramCount = taggedItems.Count(i => i is LiveTvProgram); - dto.SongCount = taggedItems.Count(i => i is Audio); - } - - dto.ChildCount = taggedItems.Count; - } - - /// <summary> - /// Attaches the user specific info. - /// </summary> - private async Task AttachUserSpecificInfo(BaseItemDto dto, BaseItem item, User user, DtoOptions dtoOptions) - { - var fields = dtoOptions.Fields; - - if (item.IsFolder) - { - var folder = (Folder)item; - - if (dtoOptions.EnableUserData) - { - dto.UserData = await _userDataRepository.GetUserDataDto(item, dto, user).ConfigureAwait(false); - } - - if (!dto.ChildCount.HasValue && item.SourceType == SourceType.Library) - { - dto.ChildCount = GetChildCount(folder, user); - } - - if (fields.Contains(ItemFields.CumulativeRunTimeTicks)) - { - dto.CumulativeRunTimeTicks = item.RunTimeTicks; - } - - if (fields.Contains(ItemFields.DateLastMediaAdded)) - { - dto.DateLastMediaAdded = folder.DateLastMediaAdded; - } - } - - else - { - if (dtoOptions.EnableUserData) - { - dto.UserData = _userDataRepository.GetUserDataDto(item, user).Result; - } - } - - dto.PlayAccess = item.GetPlayAccess(user); - - if (fields.Contains(ItemFields.BasicSyncInfo) || fields.Contains(ItemFields.SyncInfo)) - { - var userCanSync = user != null && user.Policy.EnableSync; - if (userCanSync && _syncManager.SupportsSync(item)) - { - dto.SupportsSync = true; - } - } - - if (fields.Contains(ItemFields.SeasonUserData)) - { - var episode = item as Episode; - - if (episode != null) - { - var season = episode.Season; - - if (season != null) - { - dto.SeasonUserData = await _userDataRepository.GetUserDataDto(season, user).ConfigureAwait(false); - } - } - } - - var userView = item as UserView; - if (userView != null) - { - dto.HasDynamicCategories = userView.ContainsDynamicCategories(user); - } - - var collectionFolder = item as ICollectionFolder; - if (collectionFolder != null) - { - dto.HasDynamicCategories = false; - } - } - - private int GetChildCount(Folder folder, User user) - { - // Right now this is too slow to calculate for top level folders on a per-user basis - // Just return something so that apps that are expecting a value won't think the folders are empty - if (folder is ICollectionFolder || folder is UserView) - { - return new Random().Next(1, 10); - } - - return folder.GetChildCount(user); - } - - /// <summary> - /// Gets client-side Id of a server-side BaseItem - /// </summary> - /// <param name="item">The item.</param> - /// <returns>System.String.</returns> - /// <exception cref="System.ArgumentNullException">item</exception> - public string GetDtoId(BaseItem item) - { - if (item == null) - { - throw new ArgumentNullException("item"); - } - - return item.Id.ToString("N"); - } - - /// <summary> - /// Converts a UserItemData to a DTOUserItemData - /// </summary> - /// <param name="data">The data.</param> - /// <returns>DtoUserItemData.</returns> - /// <exception cref="System.ArgumentNullException"></exception> - public UserItemDataDto GetUserItemDataDto(UserItemData data) - { - if (data == null) - { - throw new ArgumentNullException("data"); - } - - return new UserItemDataDto - { - IsFavorite = data.IsFavorite, - Likes = data.Likes, - PlaybackPositionTicks = data.PlaybackPositionTicks, - PlayCount = data.PlayCount, - Rating = data.Rating, - Played = data.Played, - LastPlayedDate = data.LastPlayedDate, - Key = data.Key - }; - } - private void SetBookProperties(BaseItemDto dto, Book item) - { - dto.SeriesName = item.SeriesName; - } - private void SetPhotoProperties(BaseItemDto dto, Photo item) - { - dto.Width = item.Width; - dto.Height = item.Height; - dto.CameraMake = item.CameraMake; - dto.CameraModel = item.CameraModel; - dto.Software = item.Software; - dto.ExposureTime = item.ExposureTime; - dto.FocalLength = item.FocalLength; - dto.ImageOrientation = item.Orientation; - dto.Aperture = item.Aperture; - dto.ShutterSpeed = item.ShutterSpeed; - - dto.Latitude = item.Latitude; - dto.Longitude = item.Longitude; - dto.Altitude = item.Altitude; - dto.IsoSpeedRating = item.IsoSpeedRating; - - var album = item.AlbumEntity; - - if (album != null) - { - dto.Album = album.Name; - dto.AlbumId = album.Id.ToString("N"); - } - } - - private void SetMusicVideoProperties(BaseItemDto dto, MusicVideo item) - { - if (!string.IsNullOrEmpty(item.Album)) - { - var parentAlbum = _libraryManager.GetItemList(new InternalItemsQuery - { - IncludeItemTypes = new[] { typeof(MusicAlbum).Name }, - Name = item.Album - - }).FirstOrDefault(); - - if (parentAlbum != null) - { - dto.AlbumId = GetDtoId(parentAlbum); - } - } - - dto.Album = item.Album; - } - - private void SetGameProperties(BaseItemDto dto, Game item) - { - dto.Players = item.PlayersSupported; - dto.GameSystem = item.GameSystem; - dto.MultiPartGameFiles = item.MultiPartGameFiles; - } - - private void SetGameSystemProperties(BaseItemDto dto, GameSystem item) - { - dto.GameSystem = item.GameSystemName; - } - - private List<string> GetImageTags(BaseItem item, List<ItemImageInfo> images) - { - return images - .Select(p => GetImageCacheTag(item, p)) - .Where(i => i != null) - .ToList(); - } - - private string GetImageCacheTag(BaseItem item, ImageType type) - { - try - { - return _imageProcessor.GetImageCacheTag(item, type); - } - catch (Exception ex) - { - _logger.ErrorException("Error getting {0} image info", ex, type); - return null; - } - } - - private string GetImageCacheTag(BaseItem item, ItemImageInfo image) - { - try - { - return _imageProcessor.GetImageCacheTag(item, image); - } - catch (Exception ex) - { - _logger.ErrorException("Error getting {0} image info for {1}", ex, image.Type, image.Path); - return null; - } - } - - /// <summary> - /// Attaches People DTO's to a DTOBaseItem - /// </summary> - /// <param name="dto">The dto.</param> - /// <param name="item">The item.</param> - /// <returns>Task.</returns> - private void AttachPeople(BaseItemDto dto, BaseItem item) - { - // Ordering by person type to ensure actors and artists are at the front. - // This is taking advantage of the fact that they both begin with A - // This should be improved in the future - var people = _libraryManager.GetPeople(item).OrderBy(i => i.SortOrder ?? int.MaxValue) - .ThenBy(i => - { - if (i.IsType(PersonType.Actor)) - { - return 0; - } - if (i.IsType(PersonType.GuestStar)) - { - return 1; - } - if (i.IsType(PersonType.Director)) - { - return 2; - } - if (i.IsType(PersonType.Writer)) - { - return 3; - } - if (i.IsType(PersonType.Producer)) - { - return 4; - } - if (i.IsType(PersonType.Composer)) - { - return 4; - } - - return 10; - }) - .ToList(); - - var list = new List<BaseItemPerson>(); - - var dictionary = people.Select(p => p.Name) - .Distinct(StringComparer.OrdinalIgnoreCase).Select(c => - { - try - { - return _libraryManager.GetPerson(c); - } - catch (Exception ex) - { - _logger.ErrorException("Error getting person {0}", ex, c); - return null; - } - - }).Where(i => i != null) - .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase) - .ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase); - - for (var i = 0; i < people.Count; i++) - { - var person = people[i]; - - var baseItemPerson = new BaseItemPerson - { - Name = person.Name, - Role = person.Role, - Type = person.Type - }; - - Person entity; - - if (dictionary.TryGetValue(person.Name, out entity)) - { - baseItemPerson.PrimaryImageTag = GetImageCacheTag(entity, ImageType.Primary); - baseItemPerson.Id = entity.Id.ToString("N"); - list.Add(baseItemPerson); - } - } - - dto.People = list.ToArray(); - } - - /// <summary> - /// Attaches the studios. - /// </summary> - /// <param name="dto">The dto.</param> - /// <param name="item">The item.</param> - /// <returns>Task.</returns> - private void AttachStudios(BaseItemDto dto, BaseItem item) - { - var studios = item.Studios.ToList(); - - dto.Studios = new StudioDto[studios.Count]; - - var dictionary = studios.Distinct(StringComparer.OrdinalIgnoreCase).Select(name => - { - try - { - return _libraryManager.GetStudio(name); - } - catch (IOException ex) - { - _logger.ErrorException("Error getting studio {0}", ex, name); - return null; - } - }) - .Where(i => i != null) - .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase) - .ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase); - - for (var i = 0; i < studios.Count; i++) - { - var studio = studios[i]; - - var studioDto = new StudioDto - { - Name = studio - }; - - Studio entity; - - if (dictionary.TryGetValue(studio, out entity)) - { - studioDto.Id = entity.Id.ToString("N"); - studioDto.PrimaryImageTag = GetImageCacheTag(entity, ImageType.Primary); - } - - dto.Studios[i] = studioDto; - } - } - - /// <summary> - /// Gets the chapter info dto. - /// </summary> - /// <param name="chapterInfo">The chapter info.</param> - /// <param name="item">The item.</param> - /// <returns>ChapterInfoDto.</returns> - private ChapterInfoDto GetChapterInfoDto(ChapterInfo chapterInfo, BaseItem item) - { - var dto = new ChapterInfoDto - { - Name = chapterInfo.Name, - StartPositionTicks = chapterInfo.StartPositionTicks - }; - - if (!string.IsNullOrEmpty(chapterInfo.ImagePath)) - { - dto.ImageTag = GetImageCacheTag(item, new ItemImageInfo - { - Path = chapterInfo.ImagePath, - Type = ImageType.Chapter, - DateModified = chapterInfo.ImageDateModified - }); - } - - return dto; - } - - public List<ChapterInfoDto> GetChapterInfoDtos(BaseItem item) - { - return _itemRepo.GetChapters(item.Id) - .Select(c => GetChapterInfoDto(c, item)) - .ToList(); - } - - /// <summary> - /// Sets simple property values on a DTOBaseItem - /// </summary> - /// <param name="dto">The dto.</param> - /// <param name="item">The item.</param> - /// <param name="owner">The owner.</param> - /// <param name="options">The options.</param> - private void AttachBasicFields(BaseItemDto dto, BaseItem item, BaseItem owner, DtoOptions options) - { - var fields = options.Fields; - - if (fields.Contains(ItemFields.DateCreated)) - { - dto.DateCreated = item.DateCreated; - } - - if (fields.Contains(ItemFields.DisplayMediaType)) - { - dto.DisplayMediaType = item.DisplayMediaType; - } - - if (fields.Contains(ItemFields.Settings)) - { - dto.LockedFields = item.LockedFields; - dto.LockData = item.IsLocked; - dto.ForcedSortName = item.ForcedSortName; - } - dto.Container = item.Container; - - var hasBudget = item as IHasBudget; - if (hasBudget != null) - { - if (fields.Contains(ItemFields.Budget)) - { - dto.Budget = hasBudget.Budget; - } - - if (fields.Contains(ItemFields.Revenue)) - { - dto.Revenue = hasBudget.Revenue; - } - } - - dto.EndDate = item.EndDate; - - if (fields.Contains(ItemFields.HomePageUrl)) - { - dto.HomePageUrl = item.HomePageUrl; - } - - if (fields.Contains(ItemFields.ExternalUrls)) - { - dto.ExternalUrls = _providerManager.GetExternalUrls(item).ToArray(); - } - - if (fields.Contains(ItemFields.Tags)) - { - dto.Tags = item.Tags; - } - - if (fields.Contains(ItemFields.Keywords)) - { - dto.Keywords = item.Keywords; - } - - var hasAspectRatio = item as IHasAspectRatio; - if (hasAspectRatio != null) - { - dto.AspectRatio = hasAspectRatio.AspectRatio; - } - - if (fields.Contains(ItemFields.Metascore)) - { - var hasMetascore = item as IHasMetascore; - if (hasMetascore != null) - { - dto.Metascore = hasMetascore.Metascore; - } - } - - if (fields.Contains(ItemFields.AwardSummary)) - { - var hasAwards = item as IHasAwards; - if (hasAwards != null) - { - dto.AwardSummary = hasAwards.AwardSummary; - } - } - - var backdropLimit = options.GetImageLimit(ImageType.Backdrop); - if (backdropLimit > 0) - { - dto.BackdropImageTags = GetImageTags(item, item.GetImages(ImageType.Backdrop).Take(backdropLimit).ToList()); - } - - if (fields.Contains(ItemFields.ScreenshotImageTags)) - { - var screenshotLimit = options.GetImageLimit(ImageType.Screenshot); - if (screenshotLimit > 0) - { - dto.ScreenshotImageTags = GetImageTags(item, item.GetImages(ImageType.Screenshot).Take(screenshotLimit).ToList()); - } - } - - if (fields.Contains(ItemFields.Genres)) - { - dto.Genres = item.Genres; - } - - if (options.EnableImages) - { - dto.ImageTags = new Dictionary<ImageType, string>(); - - // Prevent implicitly captured closure - var currentItem = item; - foreach (var image in currentItem.ImageInfos.Where(i => !currentItem.AllowsMultipleImages(i.Type)) - .ToList()) - { - if (options.GetImageLimit(image.Type) > 0) - { - var tag = GetImageCacheTag(item, image); - - if (tag != null) - { - dto.ImageTags[image.Type] = tag; - } - } - } - } - - dto.Id = GetDtoId(item); - dto.IndexNumber = item.IndexNumber; - dto.ParentIndexNumber = item.ParentIndexNumber; - - if (item.IsFolder) - { - dto.IsFolder = true; - } - else if (item is IHasMediaSources) - { - dto.IsFolder = false; - } - - dto.MediaType = item.MediaType; - dto.LocationType = item.LocationType; - if (item.IsHD.HasValue && item.IsHD.Value) - { - dto.IsHD = item.IsHD; - } - dto.Audio = item.Audio; - - if (fields.Contains(ItemFields.Settings)) - { - dto.PreferredMetadataCountryCode = item.PreferredMetadataCountryCode; - dto.PreferredMetadataLanguage = item.PreferredMetadataLanguage; - } - - dto.CriticRating = item.CriticRating; - - if (fields.Contains(ItemFields.CriticRatingSummary)) - { - dto.CriticRatingSummary = item.CriticRatingSummary; - } - - var hasTrailers = item as IHasTrailers; - if (hasTrailers != null) - { - dto.LocalTrailerCount = hasTrailers.GetTrailerIds().Count; - } - - var hasDisplayOrder = item as IHasDisplayOrder; - if (hasDisplayOrder != null) - { - dto.DisplayOrder = hasDisplayOrder.DisplayOrder; - } - - var userView = item as UserView; - if (userView != null) - { - dto.CollectionType = userView.ViewType; - } - - if (fields.Contains(ItemFields.RemoteTrailers)) - { - dto.RemoteTrailers = hasTrailers != null ? - hasTrailers.RemoteTrailers : - new List<MediaUrl>(); - } - - dto.Name = item.Name; - dto.OfficialRating = item.OfficialRating; - - if (fields.Contains(ItemFields.Overview)) - { - dto.Overview = item.Overview; - } - - if (fields.Contains(ItemFields.OriginalTitle)) - { - dto.OriginalTitle = item.OriginalTitle; - } - - if (fields.Contains(ItemFields.ShortOverview)) - { - dto.ShortOverview = item.ShortOverview; - } - - if (fields.Contains(ItemFields.ParentId)) - { - var displayParentId = item.DisplayParentId; - if (displayParentId.HasValue) - { - dto.ParentId = displayParentId.Value.ToString("N"); - } - } - - AddInheritedImages(dto, item, options, owner); - - if (fields.Contains(ItemFields.Path)) - { - dto.Path = GetMappedPath(item); - } - - dto.PremiereDate = item.PremiereDate; - dto.ProductionYear = item.ProductionYear; - - if (fields.Contains(ItemFields.ProviderIds)) - { - dto.ProviderIds = item.ProviderIds; - } - - dto.RunTimeTicks = item.RunTimeTicks; - - if (fields.Contains(ItemFields.SortName)) - { - dto.SortName = item.SortName; - } - - if (fields.Contains(ItemFields.CustomRating)) - { - dto.CustomRating = item.CustomRating; - } - - if (fields.Contains(ItemFields.Taglines)) - { - if (!string.IsNullOrWhiteSpace(item.Tagline)) - { - dto.Taglines = new List<string> { item.Tagline }; - } - - if (dto.Taglines == null) - { - dto.Taglines = new List<string>(); - } - } - - dto.Type = item.GetClientTypeName(); - dto.CommunityRating = item.CommunityRating; - - if (fields.Contains(ItemFields.VoteCount)) - { - dto.VoteCount = item.VoteCount; - } - - //if (item.IsFolder) - //{ - // var folder = (Folder)item; - - // if (fields.Contains(ItemFields.IndexOptions)) - // { - // dto.IndexOptions = folder.IndexByOptionStrings.ToArray(); - // } - //} - - var supportsPlaceHolders = item as ISupportsPlaceHolders; - if (supportsPlaceHolders != null) - { - dto.IsPlaceHolder = supportsPlaceHolders.IsPlaceHolder; - } - - // Add audio info - var audio = item as Audio; - if (audio != null) - { - dto.Album = audio.Album; - dto.ExtraType = audio.ExtraType; - - var albumParent = audio.AlbumEntity; - - if (albumParent != null) - { - dto.AlbumId = GetDtoId(albumParent); - - dto.AlbumPrimaryImageTag = GetImageCacheTag(albumParent, ImageType.Primary); - } - - //if (fields.Contains(ItemFields.MediaSourceCount)) - //{ - // Songs always have one - //} - } - - var hasArtist = item as IHasArtist; - if (hasArtist != null) - { - dto.Artists = hasArtist.Artists; - - var artistItems = _libraryManager.GetArtists(new InternalItemsQuery - { - EnableTotalRecordCount = false, - ItemIds = new[] { item.Id.ToString("N") } - }); - - dto.ArtistItems = artistItems.Items - .Select(i => - { - var artist = i.Item1; - return new NameIdPair - { - Name = artist.Name, - Id = artist.Id.ToString("N") - }; - }) - .ToList(); - - // Include artists that are not in the database yet, e.g., just added via metadata editor - var foundArtists = artistItems.Items.Select(i => i.Item1.Name).ToList(); - dto.ArtistItems.AddRange(hasArtist.Artists - .Except(foundArtists, new DistinctNameComparer()) - .Select(i => - { - // This should not be necessary but we're seeing some cases of it - if (string.IsNullOrWhiteSpace(i)) - { - return null; - } - - var artist = _libraryManager.GetArtist(i); - if (artist != null) - { - return new NameIdPair - { - Name = artist.Name, - Id = artist.Id.ToString("N") - }; - } - - return null; - - }).Where(i => i != null)); - } - - var hasAlbumArtist = item as IHasAlbumArtist; - if (hasAlbumArtist != null) - { - dto.AlbumArtist = hasAlbumArtist.AlbumArtists.FirstOrDefault(); - - var artistItems = _libraryManager.GetAlbumArtists(new InternalItemsQuery - { - EnableTotalRecordCount = false, - ItemIds = new[] { item.Id.ToString("N") } - }); - - dto.AlbumArtists = artistItems.Items - .Select(i => - { - var artist = i.Item1; - return new NameIdPair - { - Name = artist.Name, - Id = artist.Id.ToString("N") - }; - }) - .ToList(); - } - - // Add video info - var video = item as Video; - if (video != null) - { - dto.VideoType = video.VideoType; - dto.Video3DFormat = video.Video3DFormat; - dto.IsoType = video.IsoType; - - if (video.HasSubtitles) - { - dto.HasSubtitles = video.HasSubtitles; - } - - if (video.AdditionalParts.Count != 0) - { - dto.PartCount = video.AdditionalParts.Count + 1; - } - - if (fields.Contains(ItemFields.MediaSourceCount)) - { - var mediaSourceCount = video.MediaSourceCount; - if (mediaSourceCount != 1) - { - dto.MediaSourceCount = mediaSourceCount; - } - } - - if (fields.Contains(ItemFields.Chapters)) - { - dto.Chapters = GetChapterInfoDtos(item); - } - - dto.ExtraType = video.ExtraType; - } - - if (fields.Contains(ItemFields.MediaStreams)) - { - // Add VideoInfo - var iHasMediaSources = item as IHasMediaSources; - - if (iHasMediaSources != null) - { - List<MediaStream> mediaStreams; - - if (dto.MediaSources != null && dto.MediaSources.Count > 0) - { - mediaStreams = dto.MediaSources.Where(i => new Guid(i.Id) == item.Id) - .SelectMany(i => i.MediaStreams) - .ToList(); - } - else - { - mediaStreams = _mediaSourceManager().GetStaticMediaSources(iHasMediaSources, true).First().MediaStreams; - } - - dto.MediaStreams = mediaStreams; - } - } - - var hasSpecialFeatures = item as IHasSpecialFeatures; - if (hasSpecialFeatures != null) - { - var specialFeatureCount = hasSpecialFeatures.SpecialFeatureIds.Count; - - if (specialFeatureCount > 0) - { - dto.SpecialFeatureCount = specialFeatureCount; - } - } - - // Add EpisodeInfo - var episode = item as Episode; - if (episode != null) - { - dto.IndexNumberEnd = episode.IndexNumberEnd; - dto.SeriesName = episode.SeriesName; - - if (fields.Contains(ItemFields.AlternateEpisodeNumbers)) - { - dto.DvdSeasonNumber = episode.DvdSeasonNumber; - dto.DvdEpisodeNumber = episode.DvdEpisodeNumber; - dto.AbsoluteEpisodeNumber = episode.AbsoluteEpisodeNumber; - } - - if (fields.Contains(ItemFields.SpecialEpisodeNumbers)) - { - dto.AirsAfterSeasonNumber = episode.AirsAfterSeasonNumber; - dto.AirsBeforeEpisodeNumber = episode.AirsBeforeEpisodeNumber; - dto.AirsBeforeSeasonNumber = episode.AirsBeforeSeasonNumber; - } - - var seasonId = episode.SeasonId; - if (seasonId.HasValue) - { - dto.SeasonId = seasonId.Value.ToString("N"); - } - - dto.SeasonName = episode.SeasonName; - - var seriesId = episode.SeriesId; - if (seriesId.HasValue) - { - dto.SeriesId = seriesId.Value.ToString("N"); - } - - Series episodeSeries = null; - - if (fields.Contains(ItemFields.SeriesGenres)) - { - episodeSeries = episodeSeries ?? episode.Series; - if (episodeSeries != null) - { - dto.SeriesGenres = episodeSeries.Genres.ToList(); - } - } - - //if (fields.Contains(ItemFields.SeriesPrimaryImage)) - { - episodeSeries = episodeSeries ?? episode.Series; - if (episodeSeries != null) - { - dto.SeriesPrimaryImageTag = GetImageCacheTag(episodeSeries, ImageType.Primary); - } - } - - if (fields.Contains(ItemFields.SeriesStudio)) - { - episodeSeries = episodeSeries ?? episode.Series; - if (episodeSeries != null) - { - dto.SeriesStudio = episodeSeries.Studios.FirstOrDefault(); - } - } - } - - // Add SeriesInfo - var series = item as Series; - if (series != null) - { - dto.AirDays = series.AirDays; - dto.AirTime = series.AirTime; - dto.SeriesStatus = series.Status; - - dto.AnimeSeriesIndex = series.AnimeSeriesIndex; - } - - // Add SeasonInfo - var season = item as Season; - if (season != null) - { - dto.SeriesName = season.SeriesName; - - var seriesId = season.SeriesId; - if (seriesId.HasValue) - { - dto.SeriesId = seriesId.Value.ToString("N"); - } - - series = null; - - if (fields.Contains(ItemFields.SeriesStudio)) - { - series = series ?? season.Series; - if (series != null) - { - dto.SeriesStudio = series.Studios.FirstOrDefault(); - } - } - - if (fields.Contains(ItemFields.SeriesPrimaryImage)) - { - series = series ?? season.Series; - if (series != null) - { - dto.SeriesPrimaryImageTag = GetImageCacheTag(series, ImageType.Primary); - } - } - } - - var game = item as Game; - - if (game != null) - { - SetGameProperties(dto, game); - } - - var gameSystem = item as GameSystem; - - if (gameSystem != null) - { - SetGameSystemProperties(dto, gameSystem); - } - - var musicVideo = item as MusicVideo; - if (musicVideo != null) - { - SetMusicVideoProperties(dto, musicVideo); - } - - var book = item as Book; - if (book != null) - { - SetBookProperties(dto, book); - } - - if (item.ProductionLocations.Count > 0 || item is Movie) - { - dto.ProductionLocations = item.ProductionLocations.ToArray(); - } - - var photo = item as Photo; - if (photo != null) - { - SetPhotoProperties(dto, photo); - } - - dto.ChannelId = item.ChannelId; - - if (item.SourceType == SourceType.Channel && !string.IsNullOrWhiteSpace(item.ChannelId)) - { - var channel = _libraryManager.GetItemById(item.ChannelId); - if (channel != null) - { - dto.ChannelName = channel.Name; - } - } - } - - private void AddInheritedImages(BaseItemDto dto, BaseItem item, DtoOptions options, BaseItem owner) - { - var logoLimit = options.GetImageLimit(ImageType.Logo); - var artLimit = options.GetImageLimit(ImageType.Art); - var thumbLimit = options.GetImageLimit(ImageType.Thumb); - var backdropLimit = options.GetImageLimit(ImageType.Backdrop); - - if (logoLimit == 0 && artLimit == 0 && thumbLimit == 0 && backdropLimit == 0) - { - return; - } - - BaseItem parent = null; - var isFirst = true; - - while (((!dto.HasLogo && logoLimit > 0) || (!dto.HasArtImage && artLimit > 0) || (!dto.HasThumb && thumbLimit > 0) || parent is Series) && - (parent = parent ?? (isFirst ? item.GetParent() ?? owner : parent)) != null) - { - if (parent == null) - { - break; - } - - var allImages = parent.ImageInfos; - - if (logoLimit > 0 && !dto.HasLogo && dto.ParentLogoItemId == null) - { - var image = allImages.FirstOrDefault(i => i.Type == ImageType.Logo); - - if (image != null) - { - dto.ParentLogoItemId = GetDtoId(parent); - dto.ParentLogoImageTag = GetImageCacheTag(parent, image); - } - } - if (artLimit > 0 && !dto.HasArtImage && dto.ParentArtItemId == null) - { - var image = allImages.FirstOrDefault(i => i.Type == ImageType.Art); - - if (image != null) - { - dto.ParentArtItemId = GetDtoId(parent); - dto.ParentArtImageTag = GetImageCacheTag(parent, image); - } - } - if (thumbLimit > 0 && !dto.HasThumb && (dto.ParentThumbItemId == null || parent is Series)) - { - var image = allImages.FirstOrDefault(i => i.Type == ImageType.Thumb); - - if (image != null) - { - dto.ParentThumbItemId = GetDtoId(parent); - dto.ParentThumbImageTag = GetImageCacheTag(parent, image); - } - } - if (backdropLimit > 0 && !dto.HasBackdrop) - { - var images = allImages.Where(i => i.Type == ImageType.Backdrop).Take(backdropLimit).ToList(); - - if (images.Count > 0) - { - dto.ParentBackdropItemId = GetDtoId(parent); - dto.ParentBackdropImageTags = GetImageTags(parent, images); - } - } - - isFirst = false; - parent = parent.GetParent(); - } - } - - private string GetMappedPath(BaseItem item) - { - var path = item.Path; - - var locationType = item.LocationType; - - if (locationType == LocationType.FileSystem || locationType == LocationType.Offline) - { - path = _libraryManager.GetPathAfterNetworkSubstitution(path, item); - } - - return path; - } - - /// <summary> - /// Attaches the primary image aspect ratio. - /// </summary> - /// <param name="dto">The dto.</param> - /// <param name="item">The item.</param> - /// <returns>Task.</returns> - public void AttachPrimaryImageAspectRatio(IItemDto dto, IHasImages item) - { - dto.PrimaryImageAspectRatio = GetPrimaryImageAspectRatio(item); - } - - public double? GetPrimaryImageAspectRatio(IHasImages item) - { - var imageInfo = item.GetImageInfo(ImageType.Primary, 0); - - if (imageInfo == null || !imageInfo.IsLocalFile) - { - return null; - } - - ImageSize size; - - try - { - size = _imageProcessor.GetImageSize(imageInfo); - } - catch - { - //_logger.ErrorException("Failed to determine primary image aspect ratio for {0}", ex, path); - return null; - } - - var supportedEnhancers = _imageProcessor.GetSupportedEnhancers(item, ImageType.Primary).ToList(); - - foreach (var enhancer in supportedEnhancers) - { - try - { - size = enhancer.GetEnhancedImageSize(item, ImageType.Primary, 0, size); - } - catch (Exception ex) - { - _logger.ErrorException("Error in image enhancer: {0}", ex, enhancer.GetType().Name); - } - } - - var width = size.Width; - var height = size.Height; - - if (width == 0 || height == 0) - { - return null; - } - - var photo = item as Photo; - if (photo != null && photo.Orientation.HasValue) - { - switch (photo.Orientation.Value) - { - case ImageOrientation.LeftBottom: - case ImageOrientation.LeftTop: - case ImageOrientation.RightBottom: - case ImageOrientation.RightTop: - var temp = height; - height = width; - width = temp; - break; - } - } - - return width / height; - } - } -} diff --git a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs deleted file mode 100644 index 19592bc4e..000000000 --- a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs +++ /dev/null @@ -1,831 +0,0 @@ -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.FileOrganization; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Extensions; -using MediaBrowser.Model.FileOrganization; -using MediaBrowser.Model.Logging; -using MediaBrowser.Server.Implementations.Library; -using MediaBrowser.Server.Implementations.Logging; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.IO; -using MediaBrowser.Model.IO; - -namespace MediaBrowser.Server.Implementations.FileOrganization -{ - public class EpisodeFileOrganizer - { - private readonly ILibraryMonitor _libraryMonitor; - private readonly ILibraryManager _libraryManager; - private readonly ILogger _logger; - private readonly IFileSystem _fileSystem; - private readonly IFileOrganizationService _organizationService; - private readonly IServerConfigurationManager _config; - private readonly IProviderManager _providerManager; - - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - - public EpisodeFileOrganizer(IFileOrganizationService organizationService, IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IProviderManager providerManager) - { - _organizationService = organizationService; - _config = config; - _fileSystem = fileSystem; - _logger = logger; - _libraryManager = libraryManager; - _libraryMonitor = libraryMonitor; - _providerManager = providerManager; - } - - public async Task<FileOrganizationResult> OrganizeEpisodeFile(string path, AutoOrganizeOptions options, bool overwriteExisting, CancellationToken cancellationToken) - { - _logger.Info("Sorting file {0}", path); - - var result = new FileOrganizationResult - { - Date = DateTime.UtcNow, - OriginalPath = path, - OriginalFileName = Path.GetFileName(path), - Type = FileOrganizerType.Episode, - FileSize = new FileInfo(path).Length - }; - - try - { - 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 Naming.TV.EpisodeResolver(namingOptions, new PatternsLogger()); - - var episodeInfo = resolver.Resolve(path, false) ?? - new Naming.TV.EpisodeInfo(); - - var seriesName = episodeInfo.SeriesName; - - if (!string.IsNullOrEmpty(seriesName)) - { - var seasonNumber = episodeInfo.SeasonNumber; - - result.ExtractedSeasonNumber = seasonNumber; - - // Passing in true will include a few extra regex's - var episodeNumber = episodeInfo.EpisodeNumber; - - result.ExtractedEpisodeNumber = episodeNumber; - - 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) - { - _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 - { - var msg = string.Format("Unable to determine episode number 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); - - 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) - { - return previousResult; - } - } - - await _organizationService.SaveResult(result, CancellationToken.None).ConfigureAwait(false); - } - catch (Exception ex) - { - result.Status = FileSortingStatus.Failure; - result.StatusMessage = ex.Message; - } - - return result; - } - - public async Task<FileOrganizationResult> OrganizeWithCorrection(EpisodeFileOrganizationRequest request, AutoOrganizeOptions options, CancellationToken cancellationToken) - { - 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; - - int year; - if (int.TryParse(request.NewSeriesYear, out year)) - { - series.ProductionYear = year; - } - - var seriesFolderName = series.Name; - if (series.ProductionYear.HasValue) - { - seriesFolderName = string.Format("{0} ({1})", seriesFolderName, series.ProductionYear); - } - - series.Path = Path.Combine(request.TargetFolder, seriesFolderName); - - series.ProviderIds = request.NewSeriesProviderIds; - - await series.RefreshMetadata(refreshOptions, cancellationToken); - } - - 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); - } - catch (Exception ex) - { - result.Status = FileSortingStatus.Failure; - result.StatusMessage = ex.Message; - } - - return result; - } - - private Task OrganizeEpisode(string sourcePath, - string seriesName, - int? seasonNumber, - int? episodeNumber, - int? endingEpiosdeNumber, - DateTime? premiereDate, - AutoOrganizeOptions options, - bool overwriteExisting, - bool rememberCorrection, - FileOrganizationResult result, - CancellationToken cancellationToken) - { - var series = GetMatchingSeries(seriesName, result, options); - - if (series == null) - { - var msg = string.Format("Unable to find series in library matching name {0}", seriesName); - result.Status = FileSortingStatus.Failure; - result.StatusMessage = msg; - _logger.Warn(msg); - return Task.FromResult(true); - } - - return OrganizeEpisode(sourcePath, - series, - seasonNumber, - episodeNumber, - endingEpiosdeNumber, - premiereDate, - options, - overwriteExisting, - rememberCorrection, - result, - cancellationToken); - } - - private async Task OrganizeEpisode(string sourcePath, - Series series, - int? seasonNumber, - int? episodeNumber, - int? endingEpiosdeNumber, - DateTime? premiereDate, - AutoOrganizeOptions options, - bool overwriteExisting, - bool rememberCorrection, - FileOrganizationResult result, - CancellationToken cancellationToken) - { - _logger.Info("Sorting file {0} into series {1}", sourcePath, series.Path); - - var originalExtractedSeriesString = result.ExtractedName; - - bool isNew = string.IsNullOrWhiteSpace(result.Id); - - if (isNew) - { - await _organizationService.SaveResult(result, cancellationToken); - } - - if (!_organizationService.AddToInProgressList(result, isNew)) - { - 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) - { - 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; - } - - if (fileExists) - { - 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; - } - - 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; - } - } - - PerformFileSorting(options.TvOptions, result); - - if (overwriteExisting) - { - var hasRenamedFiles = false; - - foreach (var path in otherDuplicatePaths) - { - _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; - result.StatusMessage = ex.Message; - _logger.Warn(ex.Message); - return; - } - finally - { - _organizationService.RemoveFromInprogressList(result); - } - - if (rememberCorrection) - { - SaveSmartMatchString(originalExtractedSeriesString, series, options); - } - } - - private void SaveSmartMatchString(string matchString, Series series, AutoOrganizeOptions options) - { - if (string.IsNullOrEmpty(matchString) || matchString.Length < 3) - { - return; - } - - SmartMatchInfo info = options.SmartMatchInfos.FirstOrDefault(i => string.Equals(i.ItemName, series.Name, StringComparison.OrdinalIgnoreCase)); - - if (info == null) - { - info = new SmartMatchInfo(); - info.ItemName = series.Name; - info.OrganizerType = FileOrganizerType.Episode; - info.DisplayName = series.Name; - var list = options.SmartMatchInfos.ToList(); - list.Add(info); - options.SmartMatchInfos = list.ToArray(); - } - - if (!info.MatchStrings.Contains(matchString, StringComparer.OrdinalIgnoreCase)) - { - var list = info.MatchStrings.ToList(); - list.Add(matchString); - info.MatchStrings = list.ToArray(); - _config.SaveAutoOrganizeOptions(options); - } - } - - private void DeleteLibraryFile(string path, bool renameRelatedFiles, string targetPath) - { - _fileSystem.DeleteFile(path); - - if (!renameRelatedFiles) - { - return; - } - - // Now find other files - var originalFilenameWithoutExtension = Path.GetFileNameWithoutExtension(path); - var directory = Path.GetDirectoryName(path); - - if (!string.IsNullOrWhiteSpace(originalFilenameWithoutExtension) && !string.IsNullOrWhiteSpace(directory)) - { - // Get all related files, e.g. metadata, images, etc - var files = _fileSystem.GetFilePaths(directory) - .Where(i => (Path.GetFileNameWithoutExtension(i) ?? string.Empty).StartsWith(originalFilenameWithoutExtension, StringComparison.OrdinalIgnoreCase)) - .ToList(); - - var targetFilenameWithoutExtension = Path.GetFileNameWithoutExtension(targetPath); - - foreach (var file in files) - { - directory = Path.GetDirectoryName(file); - var filename = Path.GetFileName(file); - - filename = filename.Replace(originalFilenameWithoutExtension, targetFilenameWithoutExtension, - StringComparison.OrdinalIgnoreCase); - - var destination = Path.Combine(directory, filename); - - _fileSystem.MoveFile(file, destination); - } - } - } - - private List<string> GetOtherDuplicatePaths(string targetPath, - Series series, - int? seasonNumber, - int? episodeNumber, - int? endingEpisodeNumber) - { - // TODO: Support date-naming? - if (!seasonNumber.HasValue || !episodeNumber.HasValue) - { - return new List<string>(); - } - - var episodePaths = series.GetRecursiveChildren() - .OfType<Episode>() - .Where(i => - { - var locationType = i.LocationType; - - // Must be file system based and match exactly - if (locationType != LocationType.Remote && - locationType != LocationType.Virtual && - i.ParentIndexNumber.HasValue && - i.ParentIndexNumber.Value == seasonNumber && - i.IndexNumber.HasValue && - i.IndexNumber.Value == episodeNumber) - { - - if (endingEpisodeNumber.HasValue || i.IndexNumberEnd.HasValue) - { - return endingEpisodeNumber.HasValue && i.IndexNumberEnd.HasValue && - endingEpisodeNumber.Value == i.IndexNumberEnd.Value; - } - - return true; - } - - return false; - }) - .Select(i => i.Path) - .ToList(); - - var folder = Path.GetDirectoryName(targetPath); - var targetFileNameWithoutExtension = _fileSystem.GetFileNameWithoutExtension(targetPath); - - try - { - var filesOfOtherExtensions = _fileSystem.GetFilePaths(folder) - .Where(i => _libraryManager.IsVideoFile(i) && string.Equals(_fileSystem.GetFileNameWithoutExtension(i), targetFileNameWithoutExtension, StringComparison.OrdinalIgnoreCase)); - - episodePaths.AddRange(filesOfOtherExtensions); - } - catch (DirectoryNotFoundException) - { - // No big deal. Maybe the season folder doesn't already exist. - } - - return episodePaths.Where(i => !string.Equals(i, targetPath, StringComparison.OrdinalIgnoreCase)) - .Distinct(StringComparer.OrdinalIgnoreCase) - .ToList(); - } - - private void PerformFileSorting(TvFileOrganizationOptions options, FileOrganizationResult result) - { - _libraryMonitor.ReportFileSystemChangeBeginning(result.TargetPath); - - _fileSystem.CreateDirectory(Path.GetDirectoryName(result.TargetPath)); - - var targetAlreadyExists = _fileSystem.FileExists(result.TargetPath); - - try - { - if (targetAlreadyExists || options.CopyOriginalFile) - { - _fileSystem.CopyFile(result.OriginalPath, result.TargetPath, true); - } - else - { - _fileSystem.MoveFile(result.OriginalPath, result.TargetPath); - } - - result.Status = FileSortingStatus.Success; - result.StatusMessage = string.Empty; - } - catch (Exception ex) - { - var errorMsg = string.Format("Failed to move file from {0} to {1}: {2}", result.OriginalPath, result.TargetPath, ex.Message); - - result.Status = FileSortingStatus.Failure; - result.StatusMessage = errorMsg; - _logger.ErrorException(errorMsg, ex); - - return; - } - finally - { - _libraryMonitor.ReportFileSystemChangeComplete(result.TargetPath, true); - } - - if (targetAlreadyExists && !options.CopyOriginalFile) - { - try - { - _fileSystem.DeleteFile(result.OriginalPath); - } - catch (Exception ex) - { - _logger.ErrorException("Error deleting {0}", ex, result.OriginalPath); - } - } - } - - private Series GetMatchingSeries(string seriesName, FileOrganizationResult result, AutoOrganizeOptions options) - { - var parsedName = _libraryManager.ParseName(seriesName); - - var yearInName = parsedName.Year; - var nameWithoutYear = parsedName.Name; - - result.ExtractedName = nameWithoutYear; - result.ExtractedYear = yearInName; - - var series = _libraryManager.GetItemList(new Controller.Entities.InternalItemsQuery - { - IncludeItemTypes = new[] { typeof(Series).Name }, - Recursive = true - }) - .Cast<Series>() - .Select(i => NameUtils.GetMatchScore(nameWithoutYear, yearInName, i)) - .Where(i => i.Item2 > 0) - .OrderByDescending(i => i.Item2) - .Select(i => i.Item1) - .FirstOrDefault(); - - if (series == null) - { - SmartMatchInfo info = options.SmartMatchInfos.FirstOrDefault(e => e.MatchStrings.Contains(nameWithoutYear, StringComparer.OrdinalIgnoreCase)); - - if (info != null) - { - series = _libraryManager.GetItemList(new Controller.Entities.InternalItemsQuery - { - IncludeItemTypes = new[] { typeof(Series).Name }, - Recursive = true, - Name = info.ItemName - - }).Cast<Series>().FirstOrDefault(); - } - } - - return series; - } - - /// <summary> - /// Gets the new path. - /// </summary> - /// <param name="sourcePath">The source path.</param> - /// <param name="series">The series.</param> - /// <param name="seasonNumber">The season number.</param> - /// <param name="episodeNumber">The episode number.</param> - /// <param name="endingEpisodeNumber">The ending episode number.</param> - /// <param name="premiereDate">The premiere date.</param> - /// <param name="options">The options.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>System.String.</returns> - private async Task<string> GetNewPath(string sourcePath, - Series series, - int? seasonNumber, - int? episodeNumber, - int? endingEpisodeNumber, - DateTime? premiereDate, - TvFileOrganizationOptions options, - CancellationToken cancellationToken) - { - var episodeInfo = new EpisodeInfo - { - IndexNumber = episodeNumber, - IndexNumberEnd = endingEpisodeNumber, - MetadataCountryCode = series.GetPreferredMetadataCountryCode(), - MetadataLanguage = series.GetPreferredMetadataLanguage(), - ParentIndexNumber = seasonNumber, - SeriesProviderIds = series.ProviderIds, - PremiereDate = premiereDate - }; - - var searchResults = await _providerManager.GetRemoteSearchResults<Episode, EpisodeInfo>(new RemoteSearchQuery<EpisodeInfo> - { - SearchInfo = episodeInfo - - }, cancellationToken).ConfigureAwait(false); - - var episode = searchResults.FirstOrDefault(); - - if (episode == null) - { - var msg = string.Format("No provider metadata found for {0} season {1} episode {2}", series.Name, seasonNumber, episodeNumber); - _logger.Warn(msg); - throw new Exception(msg); - } - - var episodeName = episode.Name; - - //if (string.IsNullOrWhiteSpace(episodeName)) - //{ - // var msg = string.Format("No provider metadata found for {0} season {1} episode {2}", series.Name, seasonNumber, episodeNumber); - // _logger.Warn(msg); - // return null; - //} - - seasonNumber = seasonNumber ?? episode.ParentIndexNumber; - episodeNumber = episodeNumber ?? episode.IndexNumber; - - var newPath = GetSeasonFolderPath(series, seasonNumber.Value, 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.Value, episodeNumber.Value, endingEpisodeNumber, episodeName, options, maxFilenameLength); - - if (string.IsNullOrEmpty(episodeFileName)) - { - // cause failure - return string.Empty; - } - - newPath = Path.Combine(newPath, episodeFileName); - - return newPath; - } - - /// <summary> - /// Gets the season folder path. - /// </summary> - /// <param name="series">The series.</param> - /// <param name="seasonNumber">The season number.</param> - /// <param name="options">The options.</param> - /// <returns>System.String.</returns> - private string GetSeasonFolderPath(Series series, int seasonNumber, TvFileOrganizationOptions options) - { - // If there's already a season folder, use that - var season = series - .GetRecursiveChildren(i => i is Season && i.LocationType == LocationType.FileSystem && i.IndexNumber.HasValue && i.IndexNumber.Value == seasonNumber) - .FirstOrDefault(); - - if (season != null) - { - return season.Path; - } - - var path = series.Path; - - if (series.ContainsEpisodesWithoutSeasonFolders) - { - return path; - } - - if (seasonNumber == 0) - { - return Path.Combine(path, _fileSystem.GetValidFilename(options.SeasonZeroFolderName)); - } - - var seasonFolderName = options.SeasonFolderPattern - .Replace("%s", seasonNumber.ToString(_usCulture)) - .Replace("%0s", seasonNumber.ToString("00", _usCulture)) - .Replace("%00s", seasonNumber.ToString("000", _usCulture)); - - return Path.Combine(path, _fileSystem.GetValidFilename(seasonFolderName)); - } - - private string GetEpisodeFileName(string sourcePath, string seriesName, int seasonNumber, int episodeNumber, int? endingEpisodeNumber, string episodeTitle, TvFileOrganizationOptions options, int? maxLength) - { - seriesName = _fileSystem.GetValidFilename(seriesName).Trim(); - - if (string.IsNullOrWhiteSpace(episodeTitle)) - { - episodeTitle = string.Empty; - } - else - { - episodeTitle = _fileSystem.GetValidFilename(episodeTitle).Trim(); - } - - var sourceExtension = (Path.GetExtension(sourcePath) ?? string.Empty).TrimStart('.'); - - var pattern = endingEpisodeNumber.HasValue ? options.MultiEpisodeNamePattern : options.EpisodeNamePattern; - - if (string.IsNullOrWhiteSpace(pattern)) - { - throw new Exception("GetEpisodeFileName: Configured episode name pattern is empty!"); - } - - var result = pattern.Replace("%sn", seriesName) - .Replace("%s.n", seriesName.Replace(" ", ".")) - .Replace("%s_n", seriesName.Replace(" ", "_")) - .Replace("%s", seasonNumber.ToString(_usCulture)) - .Replace("%0s", seasonNumber.ToString("00", _usCulture)) - .Replace("%00s", seasonNumber.ToString("000", _usCulture)) - .Replace("%ext", sourceExtension) - .Replace("%en", "%#1") - .Replace("%e.n", "%#2") - .Replace("%e_n", "%#3"); - - if (endingEpisodeNumber.HasValue) - { - result = result.Replace("%ed", endingEpisodeNumber.Value.ToString(_usCulture)) - .Replace("%0ed", endingEpisodeNumber.Value.ToString("00", _usCulture)) - .Replace("%00ed", endingEpisodeNumber.Value.ToString("000", _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); - throw new Exception(msg); - } - - return result; - } - - private bool IsSameEpisode(string sourcePath, string newPath) - { - try - { - var sourceFileInfo = new FileInfo(sourcePath); - var destinationFileInfo = new FileInfo(newPath); - - if (sourceFileInfo.Length == destinationFileInfo.Length) - { - return true; - } - } - catch (FileNotFoundException) - { - return false; - } - catch (DirectoryNotFoundException) - { - return false; - } - - return false; - } - } -} diff --git a/MediaBrowser.Server.Implementations/FileOrganization/Extensions.cs b/MediaBrowser.Server.Implementations/FileOrganization/Extensions.cs deleted file mode 100644 index c560152db..000000000 --- a/MediaBrowser.Server.Implementations/FileOrganization/Extensions.cs +++ /dev/null @@ -1,33 +0,0 @@ -using MediaBrowser.Common.Configuration; -using MediaBrowser.Model.FileOrganization; -using System.Collections.Generic; - -namespace MediaBrowser.Server.Implementations.FileOrganization -{ - public static class ConfigurationExtension - { - public static AutoOrganizeOptions GetAutoOrganizeOptions(this IConfigurationManager manager) - { - return manager.GetConfiguration<AutoOrganizeOptions>("autoorganize"); - } - public static void SaveAutoOrganizeOptions(this IConfigurationManager manager, AutoOrganizeOptions options) - { - manager.SaveConfiguration("autoorganize", options); - } - } - - public class AutoOrganizeOptionsFactory : IConfigurationFactory - { - public IEnumerable<ConfigurationStore> GetConfigurations() - { - return new List<ConfigurationStore> - { - new ConfigurationStore - { - Key = "autoorganize", - ConfigurationType = typeof (AutoOrganizeOptions) - } - }; - } - } -} diff --git a/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationNotifier.cs b/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationNotifier.cs deleted file mode 100644 index 141dcf9b4..000000000 --- a/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationNotifier.cs +++ /dev/null @@ -1,80 +0,0 @@ -using MediaBrowser.Controller.FileOrganization; -using MediaBrowser.Controller.Plugins; -using MediaBrowser.Controller.Session; -using MediaBrowser.Model.Events; -using MediaBrowser.Model.FileOrganization; -using MediaBrowser.Model.Logging; -using System; -using System.Threading; -using MediaBrowser.Model.Tasks; - -namespace MediaBrowser.Server.Implementations.FileOrganization -{ - /// <summary> - /// Class SessionInfoWebSocketListener - /// </summary> - class FileOrganizationNotifier : IServerEntryPoint - { - private readonly IFileOrganizationService _organizationService; - private readonly ISessionManager _sessionManager; - private readonly ITaskManager _taskManager; - - public FileOrganizationNotifier(ILogger logger, IFileOrganizationService organizationService, ISessionManager sessionManager, ITaskManager taskManager) - { - _organizationService = organizationService; - _sessionManager = sessionManager; - _taskManager = taskManager; - } - - public void Run() - { - _organizationService.ItemAdded += _organizationService_ItemAdded; - _organizationService.ItemRemoved += _organizationService_ItemRemoved; - _organizationService.ItemUpdated += _organizationService_ItemUpdated; - _organizationService.LogReset += _organizationService_LogReset; - - //_taskManager.TaskCompleted += _taskManager_TaskCompleted; - } - - private void _organizationService_LogReset(object sender, EventArgs e) - { - _sessionManager.SendMessageToAdminSessions("AutoOrganize_LogReset", (FileOrganizationResult)null, CancellationToken.None); - } - - private void _organizationService_ItemUpdated(object sender, GenericEventArgs<FileOrganizationResult> e) - { - _sessionManager.SendMessageToAdminSessions("AutoOrganize_ItemUpdated", e.Argument, CancellationToken.None); - } - - private void _organizationService_ItemRemoved(object sender, GenericEventArgs<FileOrganizationResult> e) - { - _sessionManager.SendMessageToAdminSessions("AutoOrganize_ItemRemoved", e.Argument, CancellationToken.None); - } - - private void _organizationService_ItemAdded(object sender, GenericEventArgs<FileOrganizationResult> e) - { - _sessionManager.SendMessageToAdminSessions("AutoOrganize_ItemAdded", e.Argument, CancellationToken.None); - } - - //private void _taskManager_TaskCompleted(object sender, TaskCompletionEventArgs e) - //{ - // var taskWithKey = e.Task.ScheduledTask as IHasKey; - // if (taskWithKey != null && taskWithKey.Key == "AutoOrganize") - // { - // _sessionManager.SendMessageToAdminSessions("AutoOrganize_TaskCompleted", (FileOrganizationResult)null, CancellationToken.None); - // } - //} - - public void Dispose() - { - _organizationService.ItemAdded -= _organizationService_ItemAdded; - _organizationService.ItemRemoved -= _organizationService_ItemRemoved; - _organizationService.ItemUpdated -= _organizationService_ItemUpdated; - _organizationService.LogReset -= _organizationService_LogReset; - - //_taskManager.TaskCompleted -= _taskManager_TaskCompleted; - } - - - } -} diff --git a/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs b/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs deleted file mode 100644 index de33c39e6..000000000 --- a/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs +++ /dev/null @@ -1,283 +0,0 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.FileOrganization; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; -using MediaBrowser.Controller.Persistence; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.FileOrganization; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Querying; -using System; -using System.Collections.Concurrent; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Model.IO; -using MediaBrowser.Controller.Session; -using MediaBrowser.Model.Events; -using MediaBrowser.Common.Events; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.IO; -using MediaBrowser.Model.Tasks; - -namespace MediaBrowser.Server.Implementations.FileOrganization -{ - public class FileOrganizationService : IFileOrganizationService - { - private readonly ITaskManager _taskManager; - private readonly IFileOrganizationRepository _repo; - private readonly ILogger _logger; - private readonly ILibraryMonitor _libraryMonitor; - private readonly ILibraryManager _libraryManager; - private readonly IServerConfigurationManager _config; - private readonly IFileSystem _fileSystem; - private readonly IProviderManager _providerManager; - private readonly ConcurrentDictionary<string, bool> _inProgressItemIds = new ConcurrentDictionary<string, bool>(); - - public event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemAdded; - public event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemUpdated; - public event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemRemoved; - public event EventHandler LogReset; - - public FileOrganizationService(ITaskManager taskManager, IFileOrganizationRepository repo, ILogger logger, ILibraryMonitor libraryMonitor, ILibraryManager libraryManager, IServerConfigurationManager config, IFileSystem fileSystem, IProviderManager providerManager) - { - _taskManager = taskManager; - _repo = repo; - _logger = logger; - _libraryMonitor = libraryMonitor; - _libraryManager = libraryManager; - _config = config; - _fileSystem = fileSystem; - _providerManager = providerManager; - } - - public void BeginProcessNewFiles() - { - _taskManager.CancelIfRunningAndQueue<OrganizerScheduledTask>(); - } - - public Task SaveResult(FileOrganizationResult result, CancellationToken cancellationToken) - { - if (result == null || string.IsNullOrEmpty(result.OriginalPath)) - { - throw new ArgumentNullException("result"); - } - - result.Id = result.OriginalPath.GetMD5().ToString("N"); - - return _repo.SaveResult(result, cancellationToken); - } - - public QueryResult<FileOrganizationResult> GetResults(FileOrganizationResultQuery query) - { - var results = _repo.GetResults(query); - - foreach (var result in results.Items) - { - result.IsInProgress = _inProgressItemIds.ContainsKey(result.Id); - } - - return results; - } - - public FileOrganizationResult GetResult(string id) - { - var result = _repo.GetResult(id); - - if (result != null) - { - result.IsInProgress = _inProgressItemIds.ContainsKey(result.Id); - } - - return result; - } - - public FileOrganizationResult GetResultBySourcePath(string path) - { - if (string.IsNullOrEmpty(path)) - { - throw new ArgumentNullException("path"); - } - - var id = path.GetMD5().ToString("N"); - - return GetResult(id); - } - - public async Task DeleteOriginalFile(string resultId) - { - var result = _repo.GetResult(resultId); - - _logger.Info("Requested to delete {0}", result.OriginalPath); - - if (!AddToInProgressList(result, false)) - { - throw new Exception("Path is currently processed otherwise. Please try again later."); - } - - try - { - _fileSystem.DeleteFile(result.OriginalPath); - } - catch (Exception ex) - { - _logger.ErrorException("Error deleting {0}", ex, result.OriginalPath); - } - finally - { - RemoveFromInprogressList(result); - } - - await _repo.Delete(resultId); - - EventHelper.FireEventIfNotNull(ItemRemoved, this, new GenericEventArgs<FileOrganizationResult>(result), _logger); - } - - private AutoOrganizeOptions GetAutoOrganizeOptions() - { - return _config.GetAutoOrganizeOptions(); - } - - public async Task PerformOrganization(string resultId) - { - var result = _repo.GetResult(resultId); - - if (string.IsNullOrEmpty(result.TargetPath)) - { - throw new ArgumentException("No target path available."); - } - - var organizer = new EpisodeFileOrganizer(this, _config, _fileSystem, _logger, _libraryManager, - _libraryMonitor, _providerManager); - - var organizeResult = await organizer.OrganizeEpisodeFile(result.OriginalPath, GetAutoOrganizeOptions(), true, CancellationToken.None) - .ConfigureAwait(false); - - if (organizeResult.Status != FileSortingStatus.Success) - { - throw new Exception(result.StatusMessage); - } - } - - public async Task ClearLog() - { - await _repo.DeleteAll(); - EventHelper.FireEventIfNotNull(LogReset, this, EventArgs.Empty, _logger); - } - - public async Task PerformEpisodeOrganization(EpisodeFileOrganizationRequest request) - { - var organizer = new EpisodeFileOrganizer(this, _config, _fileSystem, _logger, _libraryManager, - _libraryMonitor, _providerManager); - - var result = await organizer.OrganizeWithCorrection(request, GetAutoOrganizeOptions(), CancellationToken.None).ConfigureAwait(false); - - if (result.Status != FileSortingStatus.Success) - { - throw new Exception(result.StatusMessage); - } - } - - public QueryResult<SmartMatchInfo> GetSmartMatchInfos(FileOrganizationResultQuery query) - { - if (query == null) - { - throw new ArgumentNullException("query"); - } - - var options = GetAutoOrganizeOptions(); - - var items = options.SmartMatchInfos.Skip(query.StartIndex ?? 0).Take(query.Limit ?? Int32.MaxValue).ToArray(); - - return new QueryResult<SmartMatchInfo>() - { - Items = items, - TotalRecordCount = options.SmartMatchInfos.Length - }; - } - - public void DeleteSmartMatchEntry(string itemName, string matchString) - { - if (string.IsNullOrEmpty(itemName)) - { - throw new ArgumentNullException("itemName"); - } - - if (string.IsNullOrEmpty(matchString)) - { - throw new ArgumentNullException("matchString"); - } - - var options = GetAutoOrganizeOptions(); - - SmartMatchInfo info = options.SmartMatchInfos.FirstOrDefault(i => string.Equals(i.ItemName, itemName)); - - if (info != null && info.MatchStrings.Contains(matchString)) - { - var list = info.MatchStrings.ToList(); - list.Remove(matchString); - info.MatchStrings = list.ToArray(); - - if (info.MatchStrings.Length == 0) - { - var infos = options.SmartMatchInfos.ToList(); - infos.Remove(info); - options.SmartMatchInfos = infos.ToArray(); - } - - _config.SaveAutoOrganizeOptions(options); - } - } - - /// <summary> - /// Attempts to add a an item to the list of currently processed items. - /// </summary> - /// <param name="result">The result item.</param> - /// <param name="isNewItem">Passing true will notify the client to reload all items, otherwise only a single item will be refreshed.</param> - /// <returns>True if the item was added, False if the item is already contained in the list.</returns> - public bool AddToInProgressList(FileOrganizationResult result, bool isNewItem) - { - if (string.IsNullOrWhiteSpace(result.Id)) - { - result.Id = result.OriginalPath.GetMD5().ToString("N"); - } - - if (!_inProgressItemIds.TryAdd(result.Id, false)) - { - return false; - } - - result.IsInProgress = true; - - if (isNewItem) - { - EventHelper.FireEventIfNotNull(ItemAdded, this, new GenericEventArgs<FileOrganizationResult>(result), _logger); - } - else - { - EventHelper.FireEventIfNotNull(ItemUpdated, this, new GenericEventArgs<FileOrganizationResult>(result), _logger); - } - - return true; - } - - /// <summary> - /// Removes an item from the list of currently processed items. - /// </summary> - /// <param name="result">The result item.</param> - /// <returns>True if the item was removed, False if the item was not contained in the list.</returns> - public bool RemoveFromInprogressList(FileOrganizationResult result) - { - bool itemValue; - var retval = _inProgressItemIds.TryRemove(result.Id, out itemValue); - - result.IsInProgress = false; - - EventHelper.FireEventIfNotNull(ItemUpdated, this, new GenericEventArgs<FileOrganizationResult>(result), _logger); - - return retval; - } - - } -} diff --git a/MediaBrowser.Server.Implementations/FileOrganization/NameUtils.cs b/MediaBrowser.Server.Implementations/FileOrganization/NameUtils.cs deleted file mode 100644 index 624133d4f..000000000 --- a/MediaBrowser.Server.Implementations/FileOrganization/NameUtils.cs +++ /dev/null @@ -1,96 +0,0 @@ -using MediaBrowser.Model.Extensions; -using MediaBrowser.Controller.Entities; -using System; -using System.Globalization; -using System.Linq; -using System.Text; - -namespace MediaBrowser.Server.Implementations.FileOrganization -{ - public static class NameUtils - { - private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - - internal static Tuple<T, int> GetMatchScore<T>(string sortedName, int? year, T series) - where T : BaseItem - { - var score = 0; - - var seriesNameWithoutYear = series.Name; - if (series.ProductionYear.HasValue) - { - seriesNameWithoutYear = seriesNameWithoutYear.Replace(series.ProductionYear.Value.ToString(UsCulture), String.Empty); - } - - if (IsNameMatch(sortedName, seriesNameWithoutYear)) - { - score++; - - if (year.HasValue && series.ProductionYear.HasValue) - { - if (year.Value == series.ProductionYear.Value) - { - score++; - } - else - { - // Regardless of name, return a 0 score if the years don't match - return new Tuple<T, int>(series, 0); - } - } - } - - return new Tuple<T, int>(series, score); - } - - - private static bool IsNameMatch(string name1, string name2) - { - name1 = GetComparableName(name1); - name2 = GetComparableName(name2); - - return String.Equals(name1, name2, StringComparison.OrdinalIgnoreCase); - } - - private static string GetComparableName(string name) - { - name = RemoveDiacritics(name); - - name = " " + name + " "; - - name = name.Replace(".", " ") - .Replace("_", " ") - .Replace(" and ", " ") - .Replace(".and.", " ") - .Replace("&", " ") - .Replace("!", " ") - .Replace("(", " ") - .Replace(")", " ") - .Replace(":", " ") - .Replace(",", " ") - .Replace("-", " ") - .Replace("'", " ") - .Replace("[", " ") - .Replace("]", " ") - .Replace(" a ", String.Empty, StringComparison.OrdinalIgnoreCase) - .Replace(" the ", String.Empty, StringComparison.OrdinalIgnoreCase) - .Replace(" ", String.Empty); - - return name.Trim(); - } - - /// <summary> - /// Removes the diacritics. - /// </summary> - /// <param name="text">The text.</param> - /// <returns>System.String.</returns> - private static string RemoveDiacritics(string text) - { - return String.Concat( - text.Normalize(NormalizationForm.FormD) - .Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch) != - UnicodeCategory.NonSpacingMark) - ).Normalize(NormalizationForm.FormC); - } - } -} diff --git a/MediaBrowser.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs b/MediaBrowser.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs deleted file mode 100644 index ca41db80c..000000000 --- a/MediaBrowser.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs +++ /dev/null @@ -1,101 +0,0 @@ -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.FileOrganization; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.FileOrganization; -using MediaBrowser.Model.Logging; -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.IO; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Tasks; - -namespace MediaBrowser.Server.Implementations.FileOrganization -{ - public class OrganizerScheduledTask : IScheduledTask, IConfigurableScheduledTask - { - private readonly ILibraryMonitor _libraryMonitor; - private readonly ILibraryManager _libraryManager; - private readonly ILogger _logger; - private readonly IFileSystem _fileSystem; - private readonly IServerConfigurationManager _config; - private readonly IFileOrganizationService _organizationService; - private readonly IProviderManager _providerManager; - - public OrganizerScheduledTask(ILibraryMonitor libraryMonitor, ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem, IServerConfigurationManager config, IFileOrganizationService organizationService, IProviderManager providerManager) - { - _libraryMonitor = libraryMonitor; - _libraryManager = libraryManager; - _logger = logger; - _fileSystem = fileSystem; - _config = config; - _organizationService = organizationService; - _providerManager = providerManager; - } - - public string Name - { - get { return "Organize new media files"; } - } - - public string Description - { - get { return "Processes new files available in the configured watch folder."; } - } - - public string Category - { - get { return "Library"; } - } - - private AutoOrganizeOptions GetAutoOrganizeOptions() - { - return _config.GetAutoOrganizeOptions(); - } - - public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress) - { - if (GetAutoOrganizeOptions().TvOptions.IsEnabled) - { - await new TvFolderOrganizer(_libraryManager, _logger, _fileSystem, _libraryMonitor, _organizationService, _config, _providerManager) - .Organize(GetAutoOrganizeOptions(), cancellationToken, progress).ConfigureAwait(false); - } - } - - /// <summary> - /// Creates the triggers that define when the task will run - /// </summary> - /// <returns>IEnumerable{BaseTaskTrigger}.</returns> - public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() - { - return new[] { - - // Every so often - new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromMinutes(5).Ticks} - }; - } - - public bool IsHidden - { - get { return !GetAutoOrganizeOptions().TvOptions.IsEnabled; } - } - - public bool IsEnabled - { - get { return GetAutoOrganizeOptions().TvOptions.IsEnabled; } - } - - public bool IsLogged - { - get { return false; } - } - - public string Key - { - get { return "AutoOrganize"; } - } - } -} diff --git a/MediaBrowser.Server.Implementations/FileOrganization/TvFolderOrganizer.cs b/MediaBrowser.Server.Implementations/FileOrganization/TvFolderOrganizer.cs deleted file mode 100644 index d83aee25b..000000000 --- a/MediaBrowser.Server.Implementations/FileOrganization/TvFolderOrganizer.cs +++ /dev/null @@ -1,210 +0,0 @@ -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.FileOrganization; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.FileOrganization; -using MediaBrowser.Model.Logging; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.IO; -using MediaBrowser.Model.IO; - -namespace MediaBrowser.Server.Implementations.FileOrganization -{ - public class TvFolderOrganizer - { - private readonly ILibraryMonitor _libraryMonitor; - private readonly ILibraryManager _libraryManager; - private readonly ILogger _logger; - private readonly IFileSystem _fileSystem; - private readonly IFileOrganizationService _organizationService; - private readonly IServerConfigurationManager _config; - private readonly IProviderManager _providerManager; - - public TvFolderOrganizer(ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem, ILibraryMonitor libraryMonitor, IFileOrganizationService organizationService, IServerConfigurationManager config, IProviderManager providerManager) - { - _libraryManager = libraryManager; - _logger = logger; - _fileSystem = fileSystem; - _libraryMonitor = libraryMonitor; - _organizationService = organizationService; - _config = config; - _providerManager = providerManager; - } - - 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(AutoOrganizeOptions options, CancellationToken cancellationToken, IProgress<double> progress) - { - var watchLocations = options.TvOptions.WatchLocations.ToList(); - - var eligibleFiles = watchLocations.SelectMany(GetFilesToOrganize) - .OrderBy(_fileSystem.GetCreationTimeUtc) - .Where(i => EnableOrganization(i, options.TvOptions)) - .ToList(); - - var processedFolders = new HashSet<string>(); - - progress.Report(10); - - if (eligibleFiles.Count > 0) - { - var numComplete = 0; - - foreach (var file in eligibleFiles) - { - var organizer = new EpisodeFileOrganizer(_organizationService, _config, _fileSystem, _logger, _libraryManager, - _libraryMonitor, _providerManager); - - try - { - 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)) - { - processedFolders.Add(file.DirectoryName); - } - } - catch (Exception ex) - { - _logger.ErrorException("Error organizing episode {0}", ex, file.FullName); - } - - numComplete++; - double percent = numComplete; - percent /= eligibleFiles.Count; - - progress.Report(10 + 89 * percent); - } - } - - cancellationToken.ThrowIfCancellationRequested(); - progress.Report(99); - - foreach (var path in processedFolders) - { - var deleteExtensions = options.TvOptions.LeftOverFileExtensionsToDelete - .Select(i => i.Trim().TrimStart('.')) - .Where(i => !string.IsNullOrEmpty(i)) - .Select(i => "." + i) - .ToList(); - - if (deleteExtensions.Count > 0) - { - DeleteLeftOverFiles(path, deleteExtensions); - } - - if (options.TvOptions.DeleteEmptyFolders) - { - if (!IsWatchFolder(path, watchLocations)) - { - DeleteEmptyFolders(path); - } - } - } - - progress.Report(100); - } - - /// <summary> - /// Gets the files to organize. - /// </summary> - /// <param name="path">The path.</param> - /// <returns>IEnumerable{FileInfo}.</returns> - private List<FileSystemMetadata> GetFilesToOrganize(string path) - { - try - { - return _fileSystem.GetFiles(path, true) - .ToList(); - } - catch (IOException ex) - { - _logger.ErrorException("Error getting files from {0}", ex, path); - - return new List<FileSystemMetadata>(); - } - } - - /// <summary> - /// Deletes the left over files. - /// </summary> - /// <param name="path">The path.</param> - /// <param name="extensions">The extensions.</param> - private void DeleteLeftOverFiles(string path, IEnumerable<string> extensions) - { - var eligibleFiles = _fileSystem.GetFiles(path, true) - .Where(i => extensions.Contains(i.Extension, StringComparer.OrdinalIgnoreCase)) - .ToList(); - - foreach (var file in eligibleFiles) - { - try - { - _fileSystem.DeleteFile(file.FullName); - } - catch (Exception ex) - { - _logger.ErrorException("Error deleting file {0}", ex, file.FullName); - } - } - } - - /// <summary> - /// Deletes the empty folders. - /// </summary> - /// <param name="path">The path.</param> - private void DeleteEmptyFolders(string path) - { - try - { - foreach (var d in _fileSystem.GetDirectoryPaths(path)) - { - DeleteEmptyFolders(d); - } - - var entries = _fileSystem.GetFileSystemEntryPaths(path); - - if (!entries.Any()) - { - try - { - _logger.Debug("Deleting empty directory {0}", path); - _fileSystem.DeleteDirectory(path, false); - } - catch (UnauthorizedAccessException) { } - catch (DirectoryNotFoundException) { } - } - } - 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); - } - } -}
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/IO/FileRefresher.cs b/MediaBrowser.Server.Implementations/IO/FileRefresher.cs index eeefdd65a..2742e1a26 100644 --- a/MediaBrowser.Server.Implementations/IO/FileRefresher.cs +++ b/MediaBrowser.Server.Implementations/IO/FileRefresher.cs @@ -14,7 +14,7 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Model.Extensions; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Tasks; -using MediaBrowser.Server.Implementations.ScheduledTasks; +using MediaBrowser.Model.Threading; namespace MediaBrowser.Server.Implementations.IO { @@ -26,13 +26,14 @@ namespace MediaBrowser.Server.Implementations.IO private IServerConfigurationManager ConfigurationManager { get; set; } private readonly IFileSystem _fileSystem; private readonly List<string> _affectedPaths = new List<string>(); - private Timer _timer; + private ITimer _timer; + private readonly ITimerFactory _timerFactory; private readonly object _timerLock = new object(); public string Path { get; private set; } public event EventHandler<EventArgs> Completed; - public FileRefresher(string path, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ITaskManager taskManager, ILogger logger) + public FileRefresher(string path, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ITaskManager taskManager, ILogger logger, ITimerFactory timerFactory) { logger.Debug("New file refresher created for {0}", path); Path = path; @@ -42,6 +43,7 @@ namespace MediaBrowser.Server.Implementations.IO LibraryManager = libraryManager; TaskManager = taskManager; Logger = logger; + _timerFactory = timerFactory; AddPath(path); } @@ -88,7 +90,7 @@ namespace MediaBrowser.Server.Implementations.IO if (_timer == null) { - _timer = new Timer(OnTimerCallback, null, TimeSpan.FromSeconds(ConfigurationManager.Configuration.LibraryMonitorDelay), TimeSpan.FromMilliseconds(-1)); + _timer = _timerFactory.Create(OnTimerCallback, null, TimeSpan.FromSeconds(ConfigurationManager.Configuration.LibraryMonitorDelay), TimeSpan.FromMilliseconds(-1)); } else { @@ -163,7 +165,7 @@ namespace MediaBrowser.Server.Implementations.IO // If the root folder changed, run the library task so the user can see it if (itemsToRefresh.Any(i => i is AggregateFolder)) { - TaskManager.CancelIfRunningAndQueue<RefreshMediaLibraryTask>(); + LibraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None); return; } @@ -268,11 +270,11 @@ namespace MediaBrowser.Server.Implementations.IO return false; } } - catch (DirectoryNotFoundException) - { - // File may have been deleted - return false; - } + //catch (DirectoryNotFoundException) + //{ + // // File may have been deleted + // return false; + //} catch (FileNotFoundException) { // File may have been deleted diff --git a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs index 77981b528..1f2a6255e 100644 --- a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs +++ b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs @@ -16,6 +16,7 @@ using MediaBrowser.Model.IO; using MediaBrowser.Controller; using MediaBrowser.Controller.IO; using MediaBrowser.Model.Tasks; +using MediaBrowser.Model.Threading; namespace MediaBrowser.Server.Implementations.IO { @@ -136,12 +137,12 @@ namespace MediaBrowser.Server.Implementations.IO private IServerConfigurationManager ConfigurationManager { get; set; } private readonly IFileSystem _fileSystem; - private readonly IServerApplicationHost _appHost; + private readonly ITimerFactory _timerFactory; /// <summary> /// Initializes a new instance of the <see cref="LibraryMonitor" /> class. /// </summary> - public LibraryMonitor(ILogManager logManager, ITaskManager taskManager, ILibraryManager libraryManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IServerApplicationHost appHost) + public LibraryMonitor(ILogManager logManager, ITaskManager taskManager, ILibraryManager libraryManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ITimerFactory timerFactory) { if (taskManager == null) { @@ -153,7 +154,7 @@ namespace MediaBrowser.Server.Implementations.IO Logger = logManager.GetLogger(GetType().Name); ConfigurationManager = configurationManager; _fileSystem = fileSystem; - _appHost = appHost; + _timerFactory = timerFactory; SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged; } @@ -528,7 +529,7 @@ namespace MediaBrowser.Server.Implementations.IO } } - var newRefresher = new FileRefresher(path, _fileSystem, ConfigurationManager, LibraryManager, TaskManager, Logger); + var newRefresher = new FileRefresher(path, _fileSystem, ConfigurationManager, LibraryManager, TaskManager, Logger, _timerFactory); newRefresher.Completed += NewRefresher_Completed; _activeRefreshers.Add(newRefresher); } diff --git a/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs deleted file mode 100644 index f20d87c4b..000000000 --- a/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs +++ /dev/null @@ -1,148 +0,0 @@ -using MediaBrowser.Model.Extensions; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Resolvers; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.IO; -using MediaBrowser.Model.IO; - -namespace MediaBrowser.Server.Implementations.Library -{ - /// <summary> - /// Provides the core resolver ignore rules - /// </summary> - public class CoreResolutionIgnoreRule : IResolverIgnoreRule - { - private readonly IFileSystem _fileSystem; - private readonly ILibraryManager _libraryManager; - - /// <summary> - /// Any folder named in this list will be ignored - can be added to at runtime for extensibility - /// </summary> - public static readonly List<string> IgnoreFolders = new List<string> - { - "metadata", - "ps3_update", - "ps3_vprm", - "extrafanart", - "extrathumbs", - ".actors", - ".wd_tv", - - // Synology - "@eaDir", - "eaDir", - "#recycle" - - }; - - public CoreResolutionIgnoreRule(IFileSystem fileSystem, ILibraryManager libraryManager) - { - _fileSystem = fileSystem; - _libraryManager = libraryManager; - } - - /// <summary> - /// Shoulds the ignore. - /// </summary> - /// <param name="fileInfo">The file information.</param> - /// <param name="parent">The parent.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem parent) - { - var filename = fileInfo.Name; - var isHidden = fileInfo.IsHidden; - var path = fileInfo.FullName; - - // Handle mac .DS_Store - // https://github.com/MediaBrowser/MediaBrowser/issues/427 - if (filename.IndexOf("._", StringComparison.OrdinalIgnoreCase) == 0) - { - return true; - } - - // Ignore hidden files and folders - if (isHidden) - { - if (parent == null) - { - var parentFolderName = Path.GetFileName(Path.GetDirectoryName(path)); - - if (string.Equals(parentFolderName, BaseItem.ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase)) - { - return false; - } - if (string.Equals(parentFolderName, BaseItem.ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase)) - { - return false; - } - } - - // Sometimes these are marked hidden - if (_fileSystem.IsRootPath(path)) - { - return false; - } - - return true; - } - - if (fileInfo.IsDirectory) - { - // Ignore any folders in our list - if (IgnoreFolders.Contains(filename, StringComparer.OrdinalIgnoreCase)) - { - return true; - } - - if (parent != null) - { - // Ignore trailer folders but allow it at the collection level - if (string.Equals(filename, BaseItem.TrailerFolderName, StringComparison.OrdinalIgnoreCase) && - !(parent is AggregateFolder) && !(parent is UserRootFolder)) - { - return true; - } - - if (string.Equals(filename, BaseItem.ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - - if (string.Equals(filename, BaseItem.ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - } - } - else - { - if (parent != null) - { - // Don't resolve these into audio files - if (string.Equals(_fileSystem.GetFileNameWithoutExtension(filename), BaseItem.ThemeSongFilename) && _libraryManager.IsAudioFile(filename)) - { - return true; - } - } - - // Ignore samples - var sampleFilename = " " + filename.Replace(".", " ", StringComparison.OrdinalIgnoreCase) - .Replace("-", " ", StringComparison.OrdinalIgnoreCase) - .Replace("_", " ", StringComparison.OrdinalIgnoreCase) - .Replace("!", " ", StringComparison.OrdinalIgnoreCase); - - if (sampleFilename.IndexOf(" sample ", StringComparison.OrdinalIgnoreCase) != -1) - { - return true; - } - } - - return false; - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs deleted file mode 100644 index 18feaa849..000000000 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ /dev/null @@ -1,3067 +0,0 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Progress; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.IO; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Persistence; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Controller.Resolvers; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Configuration; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Querying; -using MediaBrowser.Naming.Audio; -using MediaBrowser.Naming.Common; -using MediaBrowser.Naming.TV; -using MediaBrowser.Naming.Video; -using MediaBrowser.Server.Implementations.Library.Validators; -using MediaBrowser.Server.Implementations.Logging; -using MediaBrowser.Server.Implementations.ScheduledTasks; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Model.IO; -using MediaBrowser.Controller.Channels; -using MediaBrowser.Model.Channels; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Extensions; -using MediaBrowser.Model.Library; -using MediaBrowser.Model.Net; -using MediaBrowser.Server.Implementations.Library.Resolvers; -using SortOrder = MediaBrowser.Model.Entities.SortOrder; -using VideoResolver = MediaBrowser.Naming.Video.VideoResolver; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.IO; -using MediaBrowser.Model.Tasks; - -namespace MediaBrowser.Server.Implementations.Library -{ - /// <summary> - /// Class LibraryManager - /// </summary> - public class LibraryManager : ILibraryManager - { - /// <summary> - /// Gets or sets the postscan tasks. - /// </summary> - /// <value>The postscan tasks.</value> - private ILibraryPostScanTask[] PostscanTasks { get; set; } - - /// <summary> - /// Gets the intro providers. - /// </summary> - /// <value>The intro providers.</value> - private IIntroProvider[] IntroProviders { get; set; } - - /// <summary> - /// Gets the list of entity resolution ignore rules - /// </summary> - /// <value>The entity resolution ignore rules.</value> - private IResolverIgnoreRule[] EntityResolutionIgnoreRules { get; set; } - - /// <summary> - /// Gets the list of BasePluginFolders added by plugins - /// </summary> - /// <value>The plugin folders.</value> - private IVirtualFolderCreator[] PluginFolderCreators { get; set; } - - /// <summary> - /// Gets the list of currently registered entity resolvers - /// </summary> - /// <value>The entity resolvers enumerable.</value> - private IItemResolver[] EntityResolvers { get; set; } - private IMultiItemResolver[] MultiItemResolvers { get; set; } - - /// <summary> - /// Gets or sets the comparers. - /// </summary> - /// <value>The comparers.</value> - private IBaseItemComparer[] Comparers { get; set; } - - /// <summary> - /// Gets the active item repository - /// </summary> - /// <value>The item repository.</value> - public IItemRepository ItemRepository { get; set; } - - /// <summary> - /// Occurs when [item added]. - /// </summary> - public event EventHandler<ItemChangeEventArgs> ItemAdded; - - /// <summary> - /// Occurs when [item updated]. - /// </summary> - public event EventHandler<ItemChangeEventArgs> ItemUpdated; - - /// <summary> - /// Occurs when [item removed]. - /// </summary> - public event EventHandler<ItemChangeEventArgs> ItemRemoved; - - /// <summary> - /// The _logger - /// </summary> - private readonly ILogger _logger; - - /// <summary> - /// The _task manager - /// </summary> - private readonly ITaskManager _taskManager; - - /// <summary> - /// The _user manager - /// </summary> - private readonly IUserManager _userManager; - - /// <summary> - /// The _user data repository - /// </summary> - private readonly IUserDataManager _userDataRepository; - - /// <summary> - /// Gets or sets the configuration manager. - /// </summary> - /// <value>The configuration manager.</value> - private IServerConfigurationManager ConfigurationManager { get; set; } - - /// <summary> - /// A collection of items that may be referenced from multiple physical places in the library - /// (typically, multiple user roots). We store them here and be sure they all reference a - /// single instance. - /// </summary> - /// <value>The by reference items.</value> - private ConcurrentDictionary<Guid, BaseItem> ByReferenceItems { get; set; } - - private readonly Func<ILibraryMonitor> _libraryMonitorFactory; - private readonly Func<IProviderManager> _providerManagerFactory; - private readonly Func<IUserViewManager> _userviewManager; - public bool IsScanRunning { get; private set; } - - /// <summary> - /// The _library items cache - /// </summary> - private readonly ConcurrentDictionary<Guid, BaseItem> _libraryItemsCache; - /// <summary> - /// Gets the library items cache. - /// </summary> - /// <value>The library items cache.</value> - private ConcurrentDictionary<Guid, BaseItem> LibraryItemsCache - { - get - { - return _libraryItemsCache; - } - } - - private readonly IFileSystem _fileSystem; - - /// <summary> - /// Initializes a new instance of the <see cref="LibraryManager" /> class. - /// </summary> - /// <param name="logger">The logger.</param> - /// <param name="taskManager">The task manager.</param> - /// <param name="userManager">The user manager.</param> - /// <param name="configurationManager">The configuration manager.</param> - /// <param name="userDataRepository">The user data repository.</param> - public LibraryManager(ILogger logger, ITaskManager taskManager, IUserManager userManager, IServerConfigurationManager configurationManager, IUserDataManager userDataRepository, Func<ILibraryMonitor> libraryMonitorFactory, IFileSystem fileSystem, Func<IProviderManager> providerManagerFactory, Func<IUserViewManager> userviewManager) - { - _logger = logger; - _taskManager = taskManager; - _userManager = userManager; - ConfigurationManager = configurationManager; - _userDataRepository = userDataRepository; - _libraryMonitorFactory = libraryMonitorFactory; - _fileSystem = fileSystem; - _providerManagerFactory = providerManagerFactory; - _userviewManager = userviewManager; - ByReferenceItems = new ConcurrentDictionary<Guid, BaseItem>(); - _libraryItemsCache = new ConcurrentDictionary<Guid, BaseItem>(); - - ConfigurationManager.ConfigurationUpdated += ConfigurationUpdated; - - RecordConfigurationValues(configurationManager.Configuration); - } - - /// <summary> - /// Adds the parts. - /// </summary> - /// <param name="rules">The rules.</param> - /// <param name="pluginFolders">The plugin folders.</param> - /// <param name="resolvers">The resolvers.</param> - /// <param name="introProviders">The intro providers.</param> - /// <param name="itemComparers">The item comparers.</param> - /// <param name="postscanTasks">The postscan tasks.</param> - public void AddParts(IEnumerable<IResolverIgnoreRule> rules, - IEnumerable<IVirtualFolderCreator> pluginFolders, - IEnumerable<IItemResolver> resolvers, - IEnumerable<IIntroProvider> introProviders, - IEnumerable<IBaseItemComparer> itemComparers, - IEnumerable<ILibraryPostScanTask> postscanTasks) - { - 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; - - return hasOrder == null ? 0 : hasOrder.Order; - - }).ToArray(); - } - - /// <summary> - /// The _root folder - /// </summary> - private volatile AggregateFolder _rootFolder; - /// <summary> - /// The _root folder sync lock - /// </summary> - private readonly object _rootFolderSyncLock = new object(); - /// <summary> - /// Gets the root folder. - /// </summary> - /// <value>The root folder.</value> - public AggregateFolder RootFolder - { - get - { - if (_rootFolder == null) - { - lock (_rootFolderSyncLock) - { - if (_rootFolder == null) - { - _rootFolder = CreateRootFolder(); - } - } - } - return _rootFolder; - } - } - - /// <summary> - /// The _season zero display name - /// </summary> - private string _seasonZeroDisplayName; - - private bool _wizardCompleted; - /// <summary> - /// Records the configuration values. - /// </summary> - /// <param name="configuration">The configuration.</param> - private void RecordConfigurationValues(ServerConfiguration configuration) - { - _seasonZeroDisplayName = configuration.SeasonZeroDisplayName; - _wizardCompleted = configuration.IsStartupWizardCompleted; - } - - /// <summary> - /// Configurations the updated. - /// </summary> - /// <param name="sender">The sender.</param> - /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param> - void ConfigurationUpdated(object sender, EventArgs e) - { - var config = ConfigurationManager.Configuration; - - var newSeasonZeroName = ConfigurationManager.Configuration.SeasonZeroDisplayName; - var seasonZeroNameChanged = !string.Equals(_seasonZeroDisplayName, newSeasonZeroName, StringComparison.Ordinal); - var wizardChanged = config.IsStartupWizardCompleted != _wizardCompleted; - - RecordConfigurationValues(config); - - if (seasonZeroNameChanged || wizardChanged) - { - _taskManager.CancelIfRunningAndQueue<RefreshMediaLibraryTask>(); - } - - if (seasonZeroNameChanged) - { - Task.Run(async () => - { - await UpdateSeasonZeroNames(newSeasonZeroName, CancellationToken.None).ConfigureAwait(false); - - }); - } - } - - /// <summary> - /// Updates the season zero names. - /// </summary> - /// <param name="newName">The new name.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - private async Task UpdateSeasonZeroNames(string newName, CancellationToken cancellationToken) - { - var seasons = GetItemList(new InternalItemsQuery - { - IncludeItemTypes = new[] { typeof(Season).Name }, - Recursive = true, - IndexNumber = 0 - - }).Cast<Season>() - .Where(i => !string.Equals(i.Name, newName, StringComparison.Ordinal)) - .ToList(); - - foreach (var season in seasons) - { - season.Name = newName; - - try - { - await UpdateItem(season, ItemUpdateType.MetadataDownload, cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.ErrorException("Error saving {0}", ex, season.Path); - } - } - } - - public void RegisterItem(BaseItem item) - { - if (item == null) - { - throw new ArgumentNullException("item"); - } - RegisterItem(item.Id, item); - } - - private void RegisterItem(Guid id, BaseItem item) - { - if (item is IItemByName) - { - if (!(item is MusicArtist)) - { - return; - } - } - - if (item.IsFolder) - { - if (!(item is ICollectionFolder) && !(item is UserView) && !(item is Channel) && !(item is AggregateFolder)) - { - if (item.SourceType != SourceType.Library) - { - return; - } - } - } - else - { - if (item is Photo) - { - return; - } - } - - LibraryItemsCache.AddOrUpdate(id, item, delegate { return item; }); - } - - public async Task DeleteItem(BaseItem item, DeleteOptions options) - { - if (item == null) - { - throw new ArgumentNullException("item"); - } - - _logger.Debug("Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}", - item.GetType().Name, - item.Name ?? "Unknown name", - item.Path ?? string.Empty, - item.Id); - - var parent = item.Parent; - - var locationType = item.LocationType; - - var children = item.IsFolder - ? ((Folder)item).GetRecursiveChildren(false).ToList() - : new List<BaseItem>(); - - foreach (var metadataPath in GetMetadataPaths(item, children)) - { - _logger.Debug("Deleting path {0}", metadataPath); - - try - { - _fileSystem.DeleteDirectory(metadataPath, true); - } - catch (DirectoryNotFoundException) - { - - } - catch (Exception ex) - { - _logger.ErrorException("Error deleting {0}", ex, metadataPath); - } - } - - if (options.DeleteFileLocation && locationType != LocationType.Remote && locationType != LocationType.Virtual) - { - foreach (var path in item.GetDeletePaths().ToList()) - { - if (_fileSystem.DirectoryExists(path)) - { - _logger.Debug("Deleting path {0}", path); - _fileSystem.DeleteDirectory(path, true); - } - else if (_fileSystem.FileExists(path)) - { - _logger.Debug("Deleting path {0}", path); - _fileSystem.DeleteFile(path); - } - } - - if (parent != null) - { - await parent.ValidateChildren(new Progress<double>(), CancellationToken.None) - .ConfigureAwait(false); - } - } - else if (parent != null) - { - parent.RemoveChild(item); - } - - await ItemRepository.DeleteItem(item.Id, CancellationToken.None).ConfigureAwait(false); - foreach (var child in children) - { - await ItemRepository.DeleteItem(child.Id, CancellationToken.None).ConfigureAwait(false); - } - - BaseItem removed; - _libraryItemsCache.TryRemove(item.Id, out removed); - - ReportItemRemoved(item); - } - - private IEnumerable<string> GetMetadataPaths(BaseItem item, IEnumerable<BaseItem> children) - { - var list = new List<string> - { - item.GetInternalMetadataPath() - }; - - list.AddRange(children.Select(i => i.GetInternalMetadataPath())); - - return list; - } - - /// <summary> - /// Resolves the item. - /// </summary> - /// <param name="args">The args.</param> - /// <param name="resolvers">The resolvers.</param> - /// <returns>BaseItem.</returns> - private BaseItem ResolveItem(ItemResolveArgs args, IItemResolver[] resolvers) - { - var item = (resolvers ?? EntityResolvers).Select(r => Resolve(args, r)) - .FirstOrDefault(i => i != null); - - if (item != null) - { - ResolverHelper.SetInitialItemValues(item, args, _fileSystem, this); - } - - return item; - } - - 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; - } - } - - public Guid GetNewItemId(string key, Type type) - { - if (string.IsNullOrWhiteSpace(key)) - { - throw new ArgumentNullException("key"); - } - if (type == null) - { - throw new ArgumentNullException("type"); - } - - 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("/", "\\"); - } - - if (!ConfigurationManager.Configuration.EnableCaseSensitiveItemIds) - { - key = key.ToLower(); - } - - key = type.FullName + key; - - return key.GetMD5(); - } - - /// <summary> - /// Ensure supplied item has only one instance throughout - /// </summary> - /// <param name="item">The item.</param> - /// <returns>The proper instance to the item</returns> - public BaseItem GetOrAddByReferenceItem(BaseItem item) - { - // Add this item to our list if not there already - if (!ByReferenceItems.TryAdd(item.Id, item)) - { - // Already there - return the existing reference - item = ByReferenceItems[item.Id]; - } - return item; - } - - public BaseItem ResolvePath(FileSystemMetadata fileInfo, - Folder parent = null) - { - return ResolvePath(fileInfo, new DirectoryService(_logger, _fileSystem), null, parent); - } - - private BaseItem ResolvePath(FileSystemMetadata fileInfo, - IDirectoryService directoryService, - IItemResolver[] resolvers, - Folder parent = null, - string collectionType = null, - LibraryOptions libraryOptions = null) - { - if (fileInfo == null) - { - throw new ArgumentNullException("fileInfo"); - } - - var fullPath = fileInfo.FullName; - - if (string.IsNullOrWhiteSpace(collectionType) && parent != null) - { - collectionType = GetContentTypeOverride(fullPath, true); - } - - var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, directoryService) - { - Parent = parent, - Path = fullPath, - FileInfo = fileInfo, - CollectionType = collectionType, - LibraryOptions = libraryOptions - }; - - // Return null if ignore rules deem that we should do so - if (IgnoreFile(args.FileInfo, args.Parent)) - { - return null; - } - - // Gather child folder and files - if (args.IsDirectory) - { - var isPhysicalRoot = args.IsPhysicalRoot; - - // When resolving the root, we need it's grandchildren (children of user views) - var flattenFolderDepth = isPhysicalRoot ? 2 : 0; - - var fileSystemDictionary = FileData.GetFilteredFileSystemEntries(directoryService, args.Path, _fileSystem, _logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || args.IsVf); - - // Need to remove subpaths that may have been resolved from shortcuts - // Example: if \\server\movies exists, then strip out \\server\movies\action - if (isPhysicalRoot) - { - var paths = NormalizeRootPathList(fileSystemDictionary.Values); - - fileSystemDictionary = paths.ToDictionary(i => i.FullName); - } - - args.FileSystemDictionary = fileSystemDictionary; - } - - // Check to see if we should resolve based on our contents - if (args.IsDirectory && !ShouldResolvePathContents(args)) - { - return null; - } - - return ResolveItem(args, resolvers); - } - - private readonly List<string> _ignoredPaths = new List<string>(); - - public void RegisterIgnoredPath(string path) - { - lock (_ignoredPaths) - { - _ignoredPaths.Add(path); - } - } - public void UnRegisterIgnoredPath(string path) - { - lock (_ignoredPaths) - { - _ignoredPaths.Remove(path); - } - } - - public bool IgnoreFile(FileSystemMetadata file, BaseItem parent) - { - if (EntityResolutionIgnoreRules.Any(r => r.ShouldIgnore(file, parent))) - { - return true; - } - - //lock (_ignoredPaths) - { - if (_ignoredPaths.Contains(file.FullName, StringComparer.OrdinalIgnoreCase)) - { - return true; - } - } - return false; - } - - public IEnumerable<FileSystemMetadata> NormalizeRootPathList(IEnumerable<FileSystemMetadata> paths) - { - var originalList = paths.ToList(); - - var list = originalList.Where(i => i.IsDirectory) - .Select(i => _fileSystem.NormalizePath(i.FullName)) - .Distinct(StringComparer.OrdinalIgnoreCase) - .ToList(); - - var dupes = list.Where(subPath => !subPath.EndsWith(":\\", StringComparison.OrdinalIgnoreCase) && list.Any(i => _fileSystem.ContainsSubPath(i, subPath))) - .ToList(); - - foreach (var dupe in dupes) - { - _logger.Info("Found duplicate path: {0}", dupe); - } - - var newList = list.Except(dupes, StringComparer.OrdinalIgnoreCase).Select(_fileSystem.GetDirectoryInfo).ToList(); - newList.AddRange(originalList.Where(i => !i.IsDirectory)); - return newList; - } - - /// <summary> - /// Determines whether a path should be ignored based on its contents - called after the contents have been read - /// </summary> - /// <param name="args">The args.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - private static bool ShouldResolvePathContents(ItemResolveArgs args) - { - // Ignore any folders containing a file called .ignore - return !args.ContainsFileSystemEntryByName(".ignore"); - } - - public IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files, IDirectoryService directoryService, Folder parent, LibraryOptions libraryOptions, string collectionType) - { - return ResolvePaths(files, directoryService, parent, libraryOptions, collectionType, EntityResolvers); - } - - public IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files, - IDirectoryService directoryService, - Folder parent, - LibraryOptions libraryOptions, - string collectionType, - IItemResolver[] resolvers) - { - var fileList = files.Where(i => !IgnoreFile(i, parent)).ToList(); - - if (parent != null) - { - var multiItemResolvers = resolvers == null ? MultiItemResolvers : resolvers.OfType<IMultiItemResolver>().ToArray(); - - foreach (var resolver in multiItemResolvers) - { - var result = resolver.ResolveMultiple(parent, fileList, collectionType, directoryService); - - if (result != null && result.Items.Count > 0) - { - var items = new List<BaseItem>(); - items.AddRange(result.Items); - - foreach (var item in items) - { - ResolverHelper.SetInitialItemValues(item, parent, _fileSystem, this, directoryService); - } - items.AddRange(ResolveFileList(result.ExtraFiles, directoryService, parent, collectionType, resolvers, libraryOptions)); - return items; - } - } - } - - return ResolveFileList(fileList, directoryService, parent, collectionType, resolvers, libraryOptions); - } - - private IEnumerable<BaseItem> ResolveFileList(IEnumerable<FileSystemMetadata> fileList, - IDirectoryService directoryService, - Folder parent, - string collectionType, - IItemResolver[] resolvers, - LibraryOptions libraryOptions) - { - return fileList.Select(f => - { - try - { - return ResolvePath(f, directoryService, resolvers, parent, collectionType, libraryOptions); - } - catch (Exception ex) - { - _logger.ErrorException("Error resolving path {0}", ex, f.FullName); - return null; - } - }).Where(i => i != null); - } - - /// <summary> - /// Creates the root media folder - /// </summary> - /// <returns>AggregateFolder.</returns> - /// <exception cref="System.InvalidOperationException">Cannot create the root folder until plugins have loaded</exception> - public AggregateFolder CreateRootFolder() - { - var rootFolderPath = ConfigurationManager.ApplicationPaths.RootFolderPath; - - _fileSystem.CreateDirectory(rootFolderPath); - - var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ?? (AggregateFolder)ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath)); - - // Add in the plug-in folders - foreach (var child in PluginFolderCreators) - { - var folder = child.GetFolder(); - - if (folder != null) - { - if (folder.Id == Guid.Empty) - { - if (string.IsNullOrWhiteSpace(folder.Path)) - { - folder.Id = GetNewItemId(folder.GetType().Name, folder.GetType()); - } - else - { - folder.Id = GetNewItemId(folder.Path, folder.GetType()); - } - } - - var dbItem = GetItemById(folder.Id) as BasePluginFolder; - - if (dbItem != null && string.Equals(dbItem.Path, folder.Path, StringComparison.OrdinalIgnoreCase)) - { - folder = dbItem; - } - - if (folder.ParentId != rootFolder.Id) - { - folder.ParentId = rootFolder.Id; - var task = folder.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None); - Task.WaitAll(task); - } - - rootFolder.AddVirtualChild(folder); - - RegisterItem(folder); - } - } - - return rootFolder; - } - - private volatile UserRootFolder _userRootFolder; - private readonly object _syncLock = new object(); - public Folder GetUserRootFolder() - { - if (_userRootFolder == null) - { - lock (_syncLock) - { - if (_userRootFolder == null) - { - var userRootPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; - - _fileSystem.CreateDirectory(userRootPath); - - var tmpItem = GetItemById(GetNewItemId(userRootPath, typeof(UserRootFolder))) as UserRootFolder; - - if (tmpItem == null) - { - tmpItem = (UserRootFolder)ResolvePath(_fileSystem.GetDirectoryInfo(userRootPath)); - } - - _userRootFolder = tmpItem; - } - } - } - - return _userRootFolder; - } - - public BaseItem FindByPath(string path, bool? isFolder) - { - // If this returns multiple items it could be tricky figuring out which one is correct. - // In most cases, the newest one will be and the others obsolete but not yet cleaned up - - var query = new InternalItemsQuery - { - Path = path, - IsFolder = isFolder, - SortBy = new[] { ItemSortBy.DateCreated }, - SortOrder = SortOrder.Descending, - Limit = 1 - }; - - return GetItemList(query) - .FirstOrDefault(); - } - - /// <summary> - /// Gets a Person - /// </summary> - /// <param name="name">The name.</param> - /// <returns>Task{Person}.</returns> - public Person GetPerson(string name) - { - return CreateItemByName<Person>(Person.GetPath(name), name); - } - - /// <summary> - /// Gets a Studio - /// </summary> - /// <param name="name">The name.</param> - /// <returns>Task{Studio}.</returns> - public Studio GetStudio(string name) - { - return CreateItemByName<Studio>(Studio.GetPath(name), name); - } - - /// <summary> - /// Gets a Genre - /// </summary> - /// <param name="name">The name.</param> - /// <returns>Task{Genre}.</returns> - public Genre GetGenre(string name) - { - return CreateItemByName<Genre>(Genre.GetPath(name), name); - } - - /// <summary> - /// Gets the genre. - /// </summary> - /// <param name="name">The name.</param> - /// <returns>Task{MusicGenre}.</returns> - public MusicGenre GetMusicGenre(string name) - { - return CreateItemByName<MusicGenre>(MusicGenre.GetPath(name), name); - } - - /// <summary> - /// Gets the game genre. - /// </summary> - /// <param name="name">The name.</param> - /// <returns>Task{GameGenre}.</returns> - public GameGenre GetGameGenre(string name) - { - return CreateItemByName<GameGenre>(GameGenre.GetPath(name), name); - } - - /// <summary> - /// Gets a Year - /// </summary> - /// <param name="value">The value.</param> - /// <returns>Task{Year}.</returns> - /// <exception cref="System.ArgumentOutOfRangeException"></exception> - public Year GetYear(int value) - { - if (value <= 0) - { - throw new ArgumentOutOfRangeException("Years less than or equal to 0 are invalid."); - } - - var name = value.ToString(CultureInfo.InvariantCulture); - - return CreateItemByName<Year>(Year.GetPath(name), name); - } - - /// <summary> - /// Gets a Genre - /// </summary> - /// <param name="name">The name.</param> - /// <returns>Task{Genre}.</returns> - public MusicArtist GetArtist(string name) - { - return CreateItemByName<MusicArtist>(MusicArtist.GetPath(name), name); - } - - private T CreateItemByName<T>(string path, string name) - where T : BaseItem, new() - { - if (typeof(T) == typeof(MusicArtist)) - { - var existing = GetItemList(new InternalItemsQuery - { - IncludeItemTypes = new[] { typeof(T).Name }, - Name = name - - }).Cast<MusicArtist>() - .OrderBy(i => i.IsAccessedByName ? 1 : 0) - .Cast<T>() - .FirstOrDefault(); - - if (existing != null) - { - return existing; - } - } - - var id = GetNewItemId(path, typeof(T)); - - var item = GetItemById(id) as T; - - if (item == null) - { - item = new T - { - Name = name, - Id = id, - DateCreated = DateTime.UtcNow, - DateModified = DateTime.UtcNow, - Path = path - }; - - var task = CreateItem(item, CancellationToken.None); - Task.WaitAll(task); - } - - return item; - } - - public IEnumerable<MusicArtist> GetAlbumArtists(IEnumerable<IHasAlbumArtist> items) - { - var names = items - .SelectMany(i => i.AlbumArtists) - .DistinctNames() - .Select(i => - { - try - { - var artist = GetArtist(i); - - return artist; - } - catch - { - // Already logged at lower levels - return null; - } - }) - .Where(i => i != null); - - return names; - } - - public IEnumerable<MusicArtist> GetArtists(IEnumerable<IHasArtist> items) - { - var names = items - .SelectMany(i => i.AllArtists) - .DistinctNames() - .Select(i => - { - try - { - var artist = GetArtist(i); - - return artist; - } - catch - { - // Already logged at lower levels - return null; - } - }) - .Where(i => i != null); - - return names; - } - - /// <summary> - /// Validate and refresh the People sub-set of the IBN. - /// The items are stored in the db but not loaded into memory until actually requested by an operation. - /// </summary> - /// <param name="cancellationToken">The cancellation token.</param> - /// <param name="progress">The progress.</param> - /// <returns>Task.</returns> - public Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress) - { - // Ensure the location is available. - _fileSystem.CreateDirectory(ConfigurationManager.ApplicationPaths.PeoplePath); - - return new PeopleValidator(this, _logger, ConfigurationManager, _fileSystem).ValidatePeople(cancellationToken, progress); - } - - /// <summary> - /// Reloads the root media folder - /// </summary> - /// <param name="progress">The progress.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public Task ValidateMediaLibrary(IProgress<double> progress, CancellationToken cancellationToken) - { - // Just run the scheduled task so that the user can see it - _taskManager.CancelIfRunningAndQueue<RefreshMediaLibraryTask>(); - - return Task.FromResult(true); - } - - /// <summary> - /// Queues the library scan. - /// </summary> - public void QueueLibraryScan() - { - // Just run the scheduled task so that the user can see it - _taskManager.QueueScheduledTask<RefreshMediaLibraryTask>(); - } - - /// <summary> - /// Validates the media library internal. - /// </summary> - /// <param name="progress">The progress.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public async Task ValidateMediaLibraryInternal(IProgress<double> progress, CancellationToken cancellationToken) - { - IsScanRunning = true; - _libraryMonitorFactory().Stop(); - - try - { - await PerformLibraryValidation(progress, cancellationToken).ConfigureAwait(false); - } - finally - { - _libraryMonitorFactory().Start(); - IsScanRunning = false; - } - } - - private async Task PerformLibraryValidation(IProgress<double> progress, CancellationToken cancellationToken) - { - _logger.Info("Validating media library"); - - await RootFolder.RefreshMetadata(cancellationToken).ConfigureAwait(false); - - progress.Report(.5); - - // Start by just validating the children of the root, but go no further - await RootFolder.ValidateChildren(new Progress<double>(), cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: false); - - progress.Report(1); - - var userRoot = GetUserRootFolder(); - - await userRoot.RefreshMetadata(cancellationToken).ConfigureAwait(false); - - await userRoot.ValidateChildren(new Progress<double>(), cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: false).ConfigureAwait(false); - progress.Report(2); - - var innerProgress = new ActionableProgress<double>(); - - innerProgress.RegisterAction(pct => progress.Report(2 + pct * .73)); - - // Now validate the entire media library - await RootFolder.ValidateChildren(innerProgress, cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: true).ConfigureAwait(false); - - progress.Report(75); - - innerProgress = new ActionableProgress<double>(); - - innerProgress.RegisterAction(pct => progress.Report(75 + pct * .25)); - - // Run post-scan tasks - await RunPostScanTasks(innerProgress, cancellationToken).ConfigureAwait(false); - - progress.Report(100); - } - - /// <summary> - /// Runs the post scan tasks. - /// </summary> - /// <param name="progress">The progress.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - private async Task RunPostScanTasks(IProgress<double> progress, CancellationToken cancellationToken) - { - var tasks = PostscanTasks.ToList(); - - var numComplete = 0; - var numTasks = tasks.Count; - - foreach (var task in tasks) - { - var innerProgress = new ActionableProgress<double>(); - - // Prevent access to modified closure - var currentNumComplete = numComplete; - - innerProgress.RegisterAction(pct => - { - double innerPercent = currentNumComplete * 100 + pct; - innerPercent /= numTasks; - progress.Report(innerPercent); - }); - - _logger.Debug("Running post-scan task {0}", task.GetType().Name); - - try - { - await task.Run(innerProgress, cancellationToken).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - _logger.Info("Post-scan task cancelled: {0}", task.GetType().Name); - } - catch (Exception ex) - { - _logger.ErrorException("Error running postscan task", ex); - } - - numComplete++; - double percent = numComplete; - percent /= numTasks; - progress.Report(percent * 100); - } - - progress.Report(100); - } - - /// <summary> - /// Gets the default view. - /// </summary> - /// <returns>IEnumerable{VirtualFolderInfo}.</returns> - public IEnumerable<VirtualFolderInfo> GetVirtualFolders() - { - return GetView(ConfigurationManager.ApplicationPaths.DefaultUserViewsPath); - } - - /// <summary> - /// Gets the view. - /// </summary> - /// <param name="path">The path.</param> - /// <returns>IEnumerable{VirtualFolderInfo}.</returns> - private IEnumerable<VirtualFolderInfo> GetView(string path) - { - var topLibraryFolders = GetUserRootFolder().Children.ToList(); - - return _fileSystem.GetDirectoryPaths(path) - .Select(dir => GetVirtualFolderInfo(dir, topLibraryFolders)); - } - - private VirtualFolderInfo GetVirtualFolderInfo(string dir, List<BaseItem> allCollectionFolders) - { - var info = new VirtualFolderInfo - { - Name = Path.GetFileName(dir), - - Locations = Directory.EnumerateFiles(dir, "*.mblink", SearchOption.TopDirectoryOnly) - .Select(_fileSystem.ResolveShortcut) - .OrderBy(i => i) - .ToList(), - - CollectionType = GetCollectionType(dir) - }; - - var libraryFolder = allCollectionFolders.FirstOrDefault(i => string.Equals(i.Path, dir, StringComparison.OrdinalIgnoreCase)); - - if (libraryFolder != null && libraryFolder.HasImage(ImageType.Primary)) - { - info.PrimaryImageItemId = libraryFolder.Id.ToString("N"); - } - - if (libraryFolder != null) - { - info.ItemId = libraryFolder.Id.ToString("N"); - info.LibraryOptions = GetLibraryOptions(libraryFolder); - } - - return info; - } - - private string GetCollectionType(string path) - { - return _fileSystem.GetFiles(path, false) - .Where(i => string.Equals(i.Extension, ".collection", StringComparison.OrdinalIgnoreCase)) - .Select(i => _fileSystem.GetFileNameWithoutExtension(i)) - .FirstOrDefault(); - } - - /// <summary> - /// Gets the item by id. - /// </summary> - /// <param name="id">The id.</param> - /// <returns>BaseItem.</returns> - /// <exception cref="System.ArgumentNullException">id</exception> - public BaseItem GetItemById(Guid id) - { - if (id == Guid.Empty) - { - throw new ArgumentNullException("id"); - } - - BaseItem item; - - if (LibraryItemsCache.TryGetValue(id, out item)) - { - return item; - } - - item = RetrieveItem(id); - - //_logger.Debug("GetitemById {0}", id); - - if (item != null) - { - RegisterItem(item); - } - - return item; - } - - public IEnumerable<BaseItem> GetItemList(InternalItemsQuery query) - { - if (query.Recursive && query.ParentId.HasValue) - { - var parent = GetItemById(query.ParentId.Value); - if (parent != null) - { - SetTopParentIdsOrAncestors(query, new List<BaseItem> { parent }); - query.ParentId = null; - } - } - - if (query.User != null) - { - AddUserToQuery(query, query.User); - } - - return ItemRepository.GetItemList(query); - } - - public IEnumerable<BaseItem> GetItemList(InternalItemsQuery query, IEnumerable<string> parentIds) - { - var parents = parentIds.Select(i => GetItemById(new Guid(i))).Where(i => i != null).ToList(); - - SetTopParentIdsOrAncestors(query, parents); - - if (query.AncestorIds.Length == 0 && query.TopParentIds.Length == 0) - { - if (query.User != null) - { - AddUserToQuery(query, query.User); - } - } - - return ItemRepository.GetItemList(query); - } - - public QueryResult<BaseItem> QueryItems(InternalItemsQuery query) - { - if (query.User != null) - { - AddUserToQuery(query, query.User); - } - - if (query.EnableTotalRecordCount) - { - return ItemRepository.GetItems(query); - } - - return new QueryResult<BaseItem> - { - Items = ItemRepository.GetItemList(query).ToArray() - }; - } - - public List<Guid> GetItemIds(InternalItemsQuery query) - { - if (query.User != null) - { - AddUserToQuery(query, query.User); - } - - return ItemRepository.GetItemIdsList(query); - } - - public QueryResult<Tuple<BaseItem, ItemCounts>> GetStudios(InternalItemsQuery query) - { - if (query.User != null) - { - AddUserToQuery(query, query.User); - } - - SetTopParentOrAncestorIds(query); - return ItemRepository.GetStudios(query); - } - - public QueryResult<Tuple<BaseItem, ItemCounts>> GetGenres(InternalItemsQuery query) - { - if (query.User != null) - { - AddUserToQuery(query, query.User); - } - - SetTopParentOrAncestorIds(query); - return ItemRepository.GetGenres(query); - } - - public QueryResult<Tuple<BaseItem, ItemCounts>> GetGameGenres(InternalItemsQuery query) - { - if (query.User != null) - { - AddUserToQuery(query, query.User); - } - - SetTopParentOrAncestorIds(query); - return ItemRepository.GetGameGenres(query); - } - - public QueryResult<Tuple<BaseItem, ItemCounts>> GetMusicGenres(InternalItemsQuery query) - { - if (query.User != null) - { - AddUserToQuery(query, query.User); - } - - SetTopParentOrAncestorIds(query); - return ItemRepository.GetMusicGenres(query); - } - - public QueryResult<Tuple<BaseItem, ItemCounts>> GetAllArtists(InternalItemsQuery query) - { - if (query.User != null) - { - AddUserToQuery(query, query.User); - } - - SetTopParentOrAncestorIds(query); - return ItemRepository.GetAllArtists(query); - } - - public QueryResult<Tuple<BaseItem, ItemCounts>> GetArtists(InternalItemsQuery query) - { - if (query.User != null) - { - AddUserToQuery(query, query.User); - } - - SetTopParentOrAncestorIds(query); - return ItemRepository.GetArtists(query); - } - - private void SetTopParentOrAncestorIds(InternalItemsQuery query) - { - if (query.AncestorIds.Length == 0) - { - return; - } - - var parents = query.AncestorIds.Select(i => GetItemById(new Guid(i))).ToList(); - - if (parents.All(i => - { - if (i is ICollectionFolder || i is UserView) - { - return true; - } - - //_logger.Debug("Query requires ancestor query due to type: " + i.GetType().Name); - return false; - - })) - { - // Optimize by querying against top level views - query.TopParentIds = parents.SelectMany(i => GetTopParentsForQuery(i, query.User)).Select(i => i.Id.ToString("N")).ToArray(); - query.AncestorIds = new string[] { }; - } - } - - public QueryResult<Tuple<BaseItem, ItemCounts>> GetAlbumArtists(InternalItemsQuery query) - { - if (query.User != null) - { - AddUserToQuery(query, query.User); - } - - SetTopParentOrAncestorIds(query); - return ItemRepository.GetAlbumArtists(query); - } - - public QueryResult<BaseItem> GetItemsResult(InternalItemsQuery query) - { - if (query.Recursive && query.ParentId.HasValue) - { - var parent = GetItemById(query.ParentId.Value); - if (parent != null) - { - SetTopParentIdsOrAncestors(query, new List<BaseItem> { parent }); - query.ParentId = null; - } - } - - if (query.User != null) - { - AddUserToQuery(query, query.User); - } - - if (query.EnableTotalRecordCount) - { - return ItemRepository.GetItems(query); - } - - return new QueryResult<BaseItem> - { - Items = ItemRepository.GetItemList(query).ToArray() - }; - } - - private void SetTopParentIdsOrAncestors(InternalItemsQuery query, List<BaseItem> parents) - { - if (parents.All(i => - { - if (i is ICollectionFolder || i is UserView) - { - return true; - } - - //_logger.Debug("Query requires ancestor query due to type: " + i.GetType().Name); - return false; - - })) - { - // Optimize by querying against top level views - query.TopParentIds = parents.SelectMany(i => GetTopParentsForQuery(i, query.User)).Select(i => i.Id.ToString("N")).ToArray(); - } - else - { - // We need to be able to query from any arbitrary ancestor up the tree - query.AncestorIds = parents.SelectMany(i => i.GetIdsForAncestorQuery()).Select(i => i.ToString("N")).ToArray(); - } - } - - private void AddUserToQuery(InternalItemsQuery query, User user) - { - if (query.AncestorIds.Length == 0 && - !query.ParentId.HasValue && - query.ChannelIds.Length == 0 && - query.TopParentIds.Length == 0 && - string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey) - && query.ItemIds.Length == 0) - { - var userViews = _userviewManager().GetUserViews(new UserViewQuery - { - UserId = user.Id.ToString("N"), - IncludeHidden = true - - }, CancellationToken.None).Result.ToList(); - - query.TopParentIds = userViews.SelectMany(i => GetTopParentsForQuery(i, user)).Select(i => i.Id.ToString("N")).ToArray(); - } - } - - private IEnumerable<BaseItem> GetTopParentsForQuery(BaseItem item, User user) - { - var view = item as UserView; - - if (view != null) - { - if (string.Equals(view.ViewType, CollectionType.LiveTv)) - { - return new[] { view }; - } - if (string.Equals(view.ViewType, CollectionType.Channels)) - { - var channelResult = BaseItem.ChannelManager.GetChannelsInternal(new ChannelQuery - { - UserId = user.Id.ToString("N") - - }, CancellationToken.None).Result; - - return channelResult.Items; - } - - // Translate view into folders - if (view.DisplayParentId != Guid.Empty) - { - var displayParent = GetItemById(view.DisplayParentId); - if (displayParent != null) - { - return GetTopParentsForQuery(displayParent, user); - } - return new BaseItem[] { }; - } - if (view.ParentId != Guid.Empty) - { - var displayParent = GetItemById(view.ParentId); - if (displayParent != null) - { - return GetTopParentsForQuery(displayParent, user); - } - return new BaseItem[] { }; - } - - // Handle grouping - if (user != null && !string.IsNullOrWhiteSpace(view.ViewType) && UserView.IsEligibleForGrouping(view.ViewType)) - { - return user.RootFolder - .GetChildren(user, true) - .OfType<CollectionFolder>() - .Where(i => string.IsNullOrWhiteSpace(i.CollectionType) || string.Equals(i.CollectionType, view.ViewType, StringComparison.OrdinalIgnoreCase)) - .Where(i => user.IsFolderGrouped(i.Id)) - .SelectMany(i => GetTopParentsForQuery(i, user)); - } - return new BaseItem[] { }; - } - - var collectionFolder = item as CollectionFolder; - if (collectionFolder != null) - { - return collectionFolder.GetPhysicalParents(); - } - - var topParent = item.GetTopParent(); - if (topParent != null) - { - return new[] { topParent }; - } - return new BaseItem[] { }; - } - - /// <summary> - /// Gets the intros. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="user">The user.</param> - /// <returns>IEnumerable{System.String}.</returns> - public async Task<IEnumerable<Video>> GetIntros(BaseItem item, User 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<IEnumerable<IntroInfo>>.</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 => - { - try - { - return i.GetAllIntroFiles().ToList(); - } - catch (Exception ex) - { - _logger.ErrorException("Error getting intro files", ex); - - return new List<string>(); - } - }); - } - - /// <summary> - /// Resolves the intro. - /// </summary> - /// <param name="info">The info.</param> - /// <returns>Video.</returns> - private Video ResolveIntro(IntroInfo info) - { - Video video = null; - - if (info.ItemId.HasValue) - { - // Get an existing item by Id - video = GetItemById(info.ItemId.Value) as Video; - - if (video == null) - { - _logger.Error("Unable to locate item with Id {0}.", info.ItemId.Value); - } - } - else if (!string.IsNullOrEmpty(info.Path)) - { - try - { - // Try to resolve the path into a video - video = ResolvePath(_fileSystem.GetFileSystemInfo(info.Path)) as Video; - - if (video == null) - { - _logger.Error("Intro resolver returned null for {0}.", info.Path); - } - else - { - // Pull the saved db item that will include metadata - var dbItem = GetItemById(video.Id) as Video; - - if (dbItem != null) - { - video = dbItem; - } - else - { - return null; - } - } - } - catch (Exception ex) - { - _logger.ErrorException("Error resolving path {0}.", ex, info.Path); - } - } - else - { - _logger.Error("IntroProvider returned an IntroInfo with null Path and ItemId."); - } - - return video; - } - - /// <summary> - /// Sorts the specified sort by. - /// </summary> - /// <param name="items">The items.</param> - /// <param name="user">The user.</param> - /// <param name="sortBy">The sort by.</param> - /// <param name="sortOrder">The sort order.</param> - /// <returns>IEnumerable{BaseItem}.</returns> - public IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<string> sortBy, SortOrder sortOrder) - { - var isFirst = true; - - IOrderedEnumerable<BaseItem> orderedItems = null; - - foreach (var orderBy in sortBy.Select(o => GetComparer(o, user)).Where(c => c != null)) - { - if (isFirst) - { - orderedItems = sortOrder == SortOrder.Descending ? items.OrderByDescending(i => i, orderBy) : items.OrderBy(i => i, orderBy); - } - else - { - orderedItems = sortOrder == SortOrder.Descending ? orderedItems.ThenByDescending(i => i, orderBy) : orderedItems.ThenBy(i => i, orderBy); - } - - isFirst = false; - } - - return orderedItems ?? items; - } - - /// <summary> - /// Gets the comparer. - /// </summary> - /// <param name="name">The name.</param> - /// <param name="user">The user.</param> - /// <returns>IBaseItemComparer.</returns> - private IBaseItemComparer GetComparer(string name, User user) - { - var comparer = Comparers.FirstOrDefault(c => string.Equals(name, c.Name, StringComparison.OrdinalIgnoreCase)); - - if (comparer != null) - { - // If it requires a user, create a new one, and assign the user - if (comparer is IUserBaseItemComparer) - { - var userComparer = (IUserBaseItemComparer)Activator.CreateInstance(comparer.GetType()); - - userComparer.User = user; - userComparer.UserManager = _userManager; - userComparer.UserDataRepository = _userDataRepository; - - return userComparer; - } - } - - return comparer; - } - - /// <summary> - /// Creates the item. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public Task CreateItem(BaseItem item, CancellationToken cancellationToken) - { - return CreateItems(new[] { item }, cancellationToken); - } - - /// <summary> - /// Creates the items. - /// </summary> - /// <param name="items">The items.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public async Task CreateItems(IEnumerable<BaseItem> items, CancellationToken cancellationToken) - { - var list = items.ToList(); - - await ItemRepository.SaveItems(list, cancellationToken).ConfigureAwait(false); - - foreach (var item in list) - { - RegisterItem(item); - } - - if (ItemAdded != null) - { - foreach (var item in list) - { - try - { - ItemAdded(this, new ItemChangeEventArgs { Item = item }); - } - catch (Exception ex) - { - _logger.ErrorException("Error in ItemAdded event handler", ex); - } - } - } - } - - /// <summary> - /// Updates the item. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="updateReason">The update reason.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public async Task UpdateItem(BaseItem item, ItemUpdateType updateReason, CancellationToken cancellationToken) - { - var locationType = item.LocationType; - if (locationType != LocationType.Remote && locationType != LocationType.Virtual) - { - await _providerManagerFactory().SaveMetadata(item, updateReason).ConfigureAwait(false); - } - - item.DateLastSaved = DateTime.UtcNow; - - 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); - - RegisterItem(item); - - if (ItemUpdated != null) - { - try - { - ItemUpdated(this, new ItemChangeEventArgs - { - Item = item, - UpdateReason = updateReason - }); - } - catch (Exception ex) - { - _logger.ErrorException("Error in ItemUpdated event handler", ex); - } - } - } - - /// <summary> - /// Reports the item removed. - /// </summary> - /// <param name="item">The item.</param> - public void ReportItemRemoved(BaseItem item) - { - if (ItemRemoved != null) - { - try - { - ItemRemoved(this, new ItemChangeEventArgs { Item = item }); - } - catch (Exception ex) - { - _logger.ErrorException("Error in ItemRemoved event handler", ex); - } - } - } - - /// <summary> - /// Retrieves the item. - /// </summary> - /// <param name="id">The id.</param> - /// <returns>BaseItem.</returns> - public BaseItem RetrieveItem(Guid id) - { - return ItemRepository.RetrieveItem(id); - } - - public IEnumerable<Folder> GetCollectionFolders(BaseItem item) - { - while (!(item.GetParent() is AggregateFolder) && item.GetParent() != null) - { - item = item.GetParent(); - } - - if (item == null) - { - return new List<Folder>(); - } - - return GetUserRootFolder().Children - .OfType<Folder>() - .Where(i => string.Equals(i.Path, item.Path, StringComparison.OrdinalIgnoreCase) || i.PhysicalLocations.Contains(item.Path, StringComparer.OrdinalIgnoreCase)); - } - - public LibraryOptions GetLibraryOptions(BaseItem item) - { - var collectionFolder = item as CollectionFolder; - if (collectionFolder == null) - { - collectionFolder = GetCollectionFolders(item) - .OfType<CollectionFolder>() - .FirstOrDefault(); - } - - var options = collectionFolder == null ? new LibraryOptions() : collectionFolder.GetLibraryOptions(); - - if (options.SchemaVersion < 3) - { - options.SaveLocalMetadata = ConfigurationManager.Configuration.SaveLocalMeta; - options.EnableInternetProviders = ConfigurationManager.Configuration.EnableInternetProviders; - } - - if (options.SchemaVersion < 2) - { - var chapterOptions = ConfigurationManager.GetConfiguration<ChapterOptions>("chapters"); - options.ExtractChapterImagesDuringLibraryScan = chapterOptions.ExtractDuringLibraryScan; - - if (collectionFolder != null) - { - if (string.Equals(collectionFolder.CollectionType, "movies", StringComparison.OrdinalIgnoreCase)) - { - options.EnableChapterImageExtraction = chapterOptions.EnableMovieChapterImageExtraction; - } - else if (string.Equals(collectionFolder.CollectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase)) - { - options.EnableChapterImageExtraction = chapterOptions.EnableEpisodeChapterImageExtraction; - } - } - } - - return options; - } - - public string GetContentType(BaseItem item) - { - string configuredContentType = GetConfiguredContentType(item, false); - if (!string.IsNullOrWhiteSpace(configuredContentType)) - { - return configuredContentType; - } - configuredContentType = GetConfiguredContentType(item, true); - if (!string.IsNullOrWhiteSpace(configuredContentType)) - { - return configuredContentType; - } - return GetInheritedContentType(item); - } - - public string GetInheritedContentType(BaseItem item) - { - var type = GetTopFolderContentType(item); - - if (!string.IsNullOrWhiteSpace(type)) - { - return type; - } - - return item.GetParents() - .Select(GetConfiguredContentType) - .LastOrDefault(i => !string.IsNullOrWhiteSpace(i)); - } - - public string GetConfiguredContentType(BaseItem item) - { - return GetConfiguredContentType(item, false); - } - - public string GetConfiguredContentType(string path) - { - return GetContentTypeOverride(path, false); - } - - public string GetConfiguredContentType(BaseItem item, bool inheritConfiguredPath) - { - ICollectionFolder collectionFolder = item as ICollectionFolder; - if (collectionFolder != null) - { - return collectionFolder.CollectionType; - } - return GetContentTypeOverride(item.ContainingFolderPath, inheritConfiguredPath); - } - - private string GetContentTypeOverride(string path, bool inherit) - { - var nameValuePair = ConfigurationManager.Configuration.ContentTypes.FirstOrDefault(i => string.Equals(i.Name, path, StringComparison.OrdinalIgnoreCase) || (inherit && !string.IsNullOrWhiteSpace(i.Name) && _fileSystem.ContainsSubPath(i.Name, path))); - if (nameValuePair != null) - { - return nameValuePair.Value; - } - return null; - } - - private string GetTopFolderContentType(BaseItem item) - { - if (item == null) - { - return null; - } - - while (!(item.GetParent() is AggregateFolder) && item.GetParent() != null) - { - item = item.GetParent(); - } - - return GetUserRootFolder().Children - .OfType<ICollectionFolder>() - .Where(i => string.Equals(i.Path, item.Path, StringComparison.OrdinalIgnoreCase) || i.PhysicalLocations.Contains(item.Path)) - .Select(i => i.CollectionType) - .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i)); - } - - private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromHours(24); - //private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromMinutes(1); - - public Task<UserView> GetNamedView(User user, - string name, - string viewType, - string sortName, - CancellationToken cancellationToken) - { - return GetNamedView(user, name, null, viewType, sortName, cancellationToken); - } - - public async Task<UserView> GetNamedView(string name, - string viewType, - string sortName, - CancellationToken cancellationToken) - { - var path = Path.Combine(ConfigurationManager.ApplicationPaths.ItemsByNamePath, "views"); - - path = Path.Combine(path, _fileSystem.GetValidFilename(viewType)); - - var id = GetNewItemId(path + "_namedview_" + name, typeof(UserView)); - - var item = GetItemById(id) as UserView; - - var refresh = false; - - if (item == null || !string.Equals(item.Path, path, StringComparison.OrdinalIgnoreCase)) - { - _fileSystem.CreateDirectory(path); - - item = new UserView - { - Path = path, - Id = id, - DateCreated = DateTime.UtcNow, - Name = name, - ViewType = viewType, - ForcedSortName = sortName - }; - - await CreateItem(item, cancellationToken).ConfigureAwait(false); - - refresh = true; - } - - if (!refresh) - { - refresh = DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval; - } - - if (!refresh && item.DisplayParentId != Guid.Empty) - { - var displayParent = GetItemById(item.DisplayParentId); - refresh = displayParent != null && displayParent.DateLastSaved > item.DateLastRefreshed; - } - - if (refresh) - { - await item.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None).ConfigureAwait(false); - _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(_fileSystem) - { - // Not sure why this is necessary but need to figure it out - // View images are not getting utilized without this - ForceSave = true - }); - } - - return item; - } - - public async Task<UserView> GetNamedView(User user, - string name, - string parentId, - string viewType, - string sortName, - CancellationToken cancellationToken) - { - var idValues = "38_namedview_" + name + user.Id.ToString("N") + (parentId ?? string.Empty) + (viewType ?? string.Empty); - - var id = GetNewItemId(idValues, typeof(UserView)); - - var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N")); - - var item = GetItemById(id) as UserView; - - var isNew = false; - - if (item == null) - { - _fileSystem.CreateDirectory(path); - - item = new UserView - { - Path = path, - Id = id, - DateCreated = DateTime.UtcNow, - Name = name, - ViewType = viewType, - ForcedSortName = sortName, - UserId = user.Id - }; - - if (!string.IsNullOrWhiteSpace(parentId)) - { - item.DisplayParentId = new Guid(parentId); - } - - await CreateItem(item, cancellationToken).ConfigureAwait(false); - - isNew = true; - } - - var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval; - - if (!refresh && item.DisplayParentId != Guid.Empty) - { - var displayParent = GetItemById(item.DisplayParentId); - refresh = displayParent != null && displayParent.DateLastSaved > item.DateLastRefreshed; - } - - if (refresh) - { - _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(_fileSystem) - { - // Need to force save to increment DateLastSaved - ForceSave = true - }); - } - - return item; - } - - public async Task<UserView> GetShadowView(BaseItem parent, - string viewType, - string sortName, - CancellationToken cancellationToken) - { - if (parent == null) - { - throw new ArgumentNullException("parent"); - } - - var name = parent.Name; - var parentId = parent.Id; - - var idValues = "38_namedview_" + name + parentId + (viewType ?? string.Empty); - - var id = GetNewItemId(idValues, typeof(UserView)); - - var path = parent.Path; - - var item = GetItemById(id) as UserView; - - var isNew = false; - - if (item == null) - { - _fileSystem.CreateDirectory(path); - - item = new UserView - { - Path = path, - Id = id, - DateCreated = DateTime.UtcNow, - Name = name, - ViewType = viewType, - ForcedSortName = sortName - }; - - item.DisplayParentId = parentId; - - await CreateItem(item, cancellationToken).ConfigureAwait(false); - - isNew = true; - } - - var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval; - - if (!refresh && item.DisplayParentId != Guid.Empty) - { - var displayParent = GetItemById(item.DisplayParentId); - refresh = displayParent != null && displayParent.DateLastSaved > item.DateLastRefreshed; - } - - if (refresh) - { - _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(_fileSystem) - { - // Need to force save to increment DateLastSaved - ForceSave = true - }); - } - - return item; - } - - public async Task<UserView> GetNamedView(string name, - string parentId, - string viewType, - string sortName, - string uniqueId, - CancellationToken cancellationToken) - { - if (string.IsNullOrWhiteSpace(name)) - { - throw new ArgumentNullException("name"); - } - - var idValues = "37_namedview_" + name + (parentId ?? string.Empty) + (viewType ?? string.Empty); - if (!string.IsNullOrWhiteSpace(uniqueId)) - { - idValues += uniqueId; - } - - var id = GetNewItemId(idValues, typeof(UserView)); - - var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N")); - - var item = GetItemById(id) as UserView; - - var isNew = false; - - if (item == null) - { - _fileSystem.CreateDirectory(path); - - item = new UserView - { - Path = path, - Id = id, - DateCreated = DateTime.UtcNow, - Name = name, - ViewType = viewType, - ForcedSortName = sortName - }; - - if (!string.IsNullOrWhiteSpace(parentId)) - { - item.DisplayParentId = new Guid(parentId); - } - - await CreateItem(item, cancellationToken).ConfigureAwait(false); - - isNew = true; - } - - if (!string.Equals(viewType, item.ViewType, StringComparison.OrdinalIgnoreCase)) - { - item.ViewType = viewType; - await item.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false); - } - - var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval; - - if (!refresh && item.DisplayParentId != Guid.Empty) - { - var displayParent = GetItemById(item.DisplayParentId); - refresh = displayParent != null && displayParent.DateLastSaved > item.DateLastRefreshed; - } - - if (refresh) - { - _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(_fileSystem) - { - // Need to force save to increment DateLastSaved - ForceSave = true - }); - } - - return item; - } - - public bool IsVideoFile(string path, LibraryOptions libraryOptions) - { - var resolver = new VideoResolver(GetNamingOptions(libraryOptions), new PatternsLogger()); - return resolver.IsVideoFile(path); - } - - public bool IsVideoFile(string path) - { - return IsVideoFile(path, new LibraryOptions()); - } - - public bool IsAudioFile(string path, LibraryOptions libraryOptions) - { - var parser = new AudioFileParser(GetNamingOptions(libraryOptions)); - return parser.IsAudioFile(path); - } - - public bool IsAudioFile(string path) - { - return IsAudioFile(path, new LibraryOptions()); - } - - public int? GetSeasonNumberFromPath(string path) - { - return new SeasonPathParser(GetNamingOptions(), new RegexProvider()).Parse(path, true, true).SeasonNumber; - } - - public bool FillMissingEpisodeNumbersFromPath(Episode episode) - { - var resolver = new EpisodeResolver(GetNamingOptions(), - new PatternsLogger()); - - var isFolder = episode.VideoType == VideoType.BluRay || episode.VideoType == VideoType.Dvd || - episode.VideoType == VideoType.HdDvd; - - var locationType = episode.LocationType; - - var episodeInfo = locationType == LocationType.FileSystem || locationType == LocationType.Offline ? - resolver.Resolve(episode.Path, isFolder) : - 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 NamingOptions GetNamingOptions() - { - return GetNamingOptions(new LibraryOptions()); - } - - public NamingOptions GetNamingOptions(LibraryOptions libraryOptions) - { - var options = new ExtendedNamingOptions(); - - // These cause apps to have problems - options.AudioFileExtensions.Remove(".m3u"); - options.AudioFileExtensions.Remove(".wpl"); - - if (!libraryOptions.EnableArchiveMediaFiles) - { - options.AudioFileExtensions.Remove(".rar"); - options.AudioFileExtensions.Remove(".zip"); - } - - if (!libraryOptions.EnableArchiveMediaFiles) - { - options.VideoFileExtensions.Remove(".rar"); - options.VideoFileExtensions.Remove(".zip"); - } - - return options; - } - - public ItemLookupInfo ParseName(string name) - { - var resolver = new VideoResolver(GetNamingOptions(), new PatternsLogger()); - - 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<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService) - { - var files = owner.DetectIsInMixedFolder() ? new List<FileSystemMetadata>() : fileSystemChildren.Where(i => i.IsDirectory) - .Where(i => string.Equals(i.Name, BaseItem.TrailerFolderName, StringComparison.OrdinalIgnoreCase)) - .SelectMany(i => _fileSystem.GetFiles(i.FullName, false)) - .ToList(); - - var videoListResolver = new VideoListResolver(GetNamingOptions(), new PatternsLogger()); - - var videos = videoListResolver.Resolve(fileSystemChildren); - - 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 => _fileSystem.GetFileInfo(i.Path))); - } - - var resolvers = new IItemResolver[] - { - new GenericVideoResolver<Trailer>(this) - }; - - return ResolvePaths(files, directoryService, null, new LibraryOptions(), null, resolvers) - .OfType<Trailer>() - .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 Trailer; - - if (dbItem != null) - { - video = dbItem; - } - - video.ExtraType = ExtraType.Trailer; - video.TrailerTypes = new List<TrailerType> { TrailerType.LocalTrailer }; - - return video; - - // Sort them so that the list can be easily compared for changes - }).OrderBy(i => i.Path).ToList(); - } - - public IEnumerable<Video> FindExtras(BaseItem owner, List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService) - { - var files = fileSystemChildren.Where(i => i.IsDirectory) - .Where(i => string.Equals(i.Name, "extras", StringComparison.OrdinalIgnoreCase) || string.Equals(i.Name, "specials", StringComparison.OrdinalIgnoreCase)) - .SelectMany(i => _fileSystem.GetFiles(i.FullName, false)) - .ToList(); - - var videoListResolver = new VideoListResolver(GetNamingOptions(), new PatternsLogger()); - - var videos = videoListResolver.Resolve(fileSystemChildren); - - 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 => _fileSystem.GetFileInfo(i.Path))); - } - - return ResolvePaths(files, directoryService, null, new LibraryOptions(), 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(); - } - - public string GetPathAfterNetworkSubstitution(string path, BaseItem ownerItem) - { - if (ownerItem != null) - { - var libraryOptions = GetLibraryOptions(ownerItem); - if (libraryOptions != null) - { - foreach (var pathInfo in libraryOptions.PathInfos) - { - if (string.IsNullOrWhiteSpace(pathInfo.NetworkPath)) - { - continue; - } - - var substitutionResult = SubstitutePathInternal(path, pathInfo.Path, pathInfo.NetworkPath); - if (substitutionResult.Item2) - { - return substitutionResult.Item1; - } - } - } - } - - var metadataPath = ConfigurationManager.Configuration.MetadataPath; - var metadataNetworkPath = ConfigurationManager.Configuration.MetadataNetworkPath; - - if (!string.IsNullOrWhiteSpace(metadataPath) && !string.IsNullOrWhiteSpace(metadataNetworkPath)) - { - var metadataSubstitutionResult = SubstitutePathInternal(path, metadataPath, metadataNetworkPath); - if (metadataSubstitutionResult.Item2) - { - return metadataSubstitutionResult.Item1; - } - } - - foreach (var map in ConfigurationManager.Configuration.PathSubstitutions) - { - var substitutionResult = SubstitutePathInternal(path, map.From, map.To); - if (substitutionResult.Item2) - { - return substitutionResult.Item1; - } - } - - return path; - } - - public string SubstitutePath(string path, string from, string to) - { - return SubstitutePathInternal(path, from, to).Item1; - } - - private Tuple<string, bool> SubstitutePathInternal(string path, string from, string to) - { - if (string.IsNullOrWhiteSpace(path)) - { - throw new ArgumentNullException("path"); - } - if (string.IsNullOrWhiteSpace(from)) - { - throw new ArgumentNullException("from"); - } - if (string.IsNullOrWhiteSpace(to)) - { - throw new ArgumentNullException("to"); - } - - from = from.Trim(); - to = to.Trim(); - - var newPath = path.Replace(from, to, StringComparison.OrdinalIgnoreCase); - var changed = false; - - if (!string.Equals(newPath, path)) - { - if (to.IndexOf('/') != -1) - { - newPath = newPath.Replace('\\', '/'); - } - else - { - newPath = newPath.Replace('/', '\\'); - } - - changed = true; - } - - return new Tuple<string, bool>(newPath, changed); - } - - private void SetExtraTypeFromFilename(Video item) - { - var resolver = new ExtraResolver(GetNamingOptions(), new PatternsLogger(), 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; - } - } - - public List<PersonInfo> GetPeople(InternalPeopleQuery query) - { - return ItemRepository.GetPeople(query); - } - - public List<PersonInfo> GetPeople(BaseItem item) - { - if (item.SupportsPeople) - { - var people = GetPeople(new InternalPeopleQuery - { - ItemId = item.Id - }); - - if (people.Count > 0) - { - return people; - } - } - - return new List<PersonInfo>(); - } - - public List<Person> GetPeopleItems(InternalPeopleQuery query) - { - return ItemRepository.GetPeopleNames(query).Select(i => - { - try - { - return GetPerson(i); - } - catch (Exception ex) - { - _logger.ErrorException("Error getting person", ex); - return null; - } - - }).Where(i => i != null).ToList(); - } - - public List<string> GetPeopleNames(InternalPeopleQuery query) - { - return ItemRepository.GetPeopleNames(query); - } - - public Task UpdatePeople(BaseItem item, List<PersonInfo> people) - { - if (!item.SupportsPeople) - { - return Task.FromResult(true); - } - - return ItemRepository.UpdatePeople(item.Id, people); - } - - private readonly SemaphoreSlim _dynamicImageResourcePool = new SemaphoreSlim(1, 1); - public async Task<ItemImageInfo> ConvertImageToLocal(IHasImages item, ItemImageInfo image, int imageIndex) - { - foreach (var url in image.Path.Split('|')) - { - try - { - _logger.Debug("ConvertImageToLocal item {0} - image url: {1}", item.Id, url); - - await _providerManagerFactory().SaveImage(item, url, _dynamicImageResourcePool, image.Type, imageIndex, CancellationToken.None).ConfigureAwait(false); - - var newImage = item.GetImageInfo(image.Type, imageIndex); - - if (newImage != null) - { - newImage.IsPlaceholder = image.IsPlaceholder; - } - - await item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false); - - return item.GetImageInfo(image.Type, imageIndex); - } - catch (HttpException ex) - { - if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound) - { - continue; - } - throw; - } - } - - // Remove this image to prevent it from retrying over and over - item.RemoveImage(image); - await item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false); - - throw new InvalidOperationException(); - } - - public void AddVirtualFolder(string name, string collectionType, LibraryOptions options, bool refreshLibrary) - { - if (string.IsNullOrWhiteSpace(name)) - { - throw new ArgumentNullException("name"); - } - - name = _fileSystem.GetValidFilename(name); - - var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; - - var virtualFolderPath = Path.Combine(rootFolderPath, name); - while (_fileSystem.DirectoryExists(virtualFolderPath)) - { - name += "1"; - virtualFolderPath = Path.Combine(rootFolderPath, name); - } - - var mediaPathInfos = options.PathInfos; - if (mediaPathInfos != null) - { - var invalidpath = mediaPathInfos.FirstOrDefault(i => !_fileSystem.DirectoryExists(i.Path)); - if (invalidpath != null) - { - throw new ArgumentException("The specified path does not exist: " + invalidpath.Path + "."); - } - } - - _libraryMonitorFactory().Stop(); - - try - { - _fileSystem.CreateDirectory(virtualFolderPath); - - if (!string.IsNullOrEmpty(collectionType)) - { - var path = Path.Combine(virtualFolderPath, collectionType + ".collection"); - - using (File.Create(path)) - { - - } - } - - CollectionFolder.SaveLibraryOptions(virtualFolderPath, options); - - if (mediaPathInfos != null) - { - foreach (var path in mediaPathInfos) - { - AddMediaPathInternal(name, path, false); - } - } - } - finally - { - Task.Run(() => - { - // No need to start if scanning the library because it will handle it - if (refreshLibrary) - { - ValidateMediaLibrary(new Progress<double>(), CancellationToken.None); - } - else - { - // Need to add a delay here or directory watchers may still pick up the changes - var task = Task.Delay(1000); - // Have to block here to allow exceptions to bubble - Task.WaitAll(task); - - _libraryMonitorFactory().Start(); - } - }); - } - } - - private bool ValidateNetworkPath(string path) - { - if (Environment.OSVersion.Platform == PlatformID.Win32NT) - { - // We can't validate protocol-based paths, so just allow them - if (path.IndexOf("://", StringComparison.OrdinalIgnoreCase) == -1) - { - return Directory.Exists(path); - } - } - - // Without native support for unc, we cannot validate this when running under mono - return true; - } - - private const string ShortcutFileExtension = ".mblink"; - private const string ShortcutFileSearch = "*" + ShortcutFileExtension; - public void AddMediaPath(string virtualFolderName, MediaPathInfo pathInfo) - { - AddMediaPathInternal(virtualFolderName, pathInfo, true); - } - - private void AddMediaPathInternal(string virtualFolderName, MediaPathInfo pathInfo, bool saveLibraryOptions) - { - if (pathInfo == null) - { - throw new ArgumentNullException("path"); - } - - var path = pathInfo.Path; - - if (string.IsNullOrWhiteSpace(path)) - { - throw new ArgumentNullException("path"); - } - - if (!_fileSystem.DirectoryExists(path)) - { - throw new DirectoryNotFoundException("The path does not exist."); - } - - if (!string.IsNullOrWhiteSpace(pathInfo.NetworkPath) && !ValidateNetworkPath(pathInfo.NetworkPath)) - { - throw new DirectoryNotFoundException("The network path does not exist."); - } - - var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; - var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName); - - var shortcutFilename = _fileSystem.GetFileNameWithoutExtension(path); - - var lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension); - - while (_fileSystem.FileExists(lnk)) - { - shortcutFilename += "1"; - lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension); - } - - _fileSystem.CreateShortcut(lnk, path); - - RemoveContentTypeOverrides(path); - - if (saveLibraryOptions) - { - var libraryOptions = CollectionFolder.GetLibraryOptions(virtualFolderPath); - - var list = libraryOptions.PathInfos.ToList(); - list.Add(pathInfo); - libraryOptions.PathInfos = list.ToArray(); - - SyncLibraryOptionsToLocations(virtualFolderPath, libraryOptions); - - CollectionFolder.SaveLibraryOptions(virtualFolderPath, libraryOptions); - } - } - - public void UpdateMediaPath(string virtualFolderName, MediaPathInfo pathInfo) - { - if (pathInfo == null) - { - throw new ArgumentNullException("path"); - } - - if (!string.IsNullOrWhiteSpace(pathInfo.NetworkPath) && !ValidateNetworkPath(pathInfo.NetworkPath)) - { - throw new DirectoryNotFoundException("The network path does not exist."); - } - - var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; - var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName); - - var libraryOptions = CollectionFolder.GetLibraryOptions(virtualFolderPath); - - SyncLibraryOptionsToLocations(virtualFolderPath, libraryOptions); - - var list = libraryOptions.PathInfos.ToList(); - foreach (var originalPathInfo in list) - { - if (string.Equals(pathInfo.Path, originalPathInfo.Path, StringComparison.Ordinal)) - { - originalPathInfo.NetworkPath = pathInfo.NetworkPath; - break; - } - } - - libraryOptions.PathInfos = list.ToArray(); - - CollectionFolder.SaveLibraryOptions(virtualFolderPath, libraryOptions); - } - - private void SyncLibraryOptionsToLocations(string virtualFolderPath, LibraryOptions options) - { - var topLibraryFolders = GetUserRootFolder().Children.ToList(); - var info = GetVirtualFolderInfo(virtualFolderPath, topLibraryFolders); - - if (info.Locations.Count > 0 && info.Locations.Count != options.PathInfos.Length) - { - var list = options.PathInfos.ToList(); - - foreach (var location in info.Locations) - { - if (!list.Any(i => string.Equals(i.Path, location, StringComparison.Ordinal))) - { - list.Add(new MediaPathInfo - { - Path = location - }); - } - } - - options.PathInfos = list.ToArray(); - } - } - - public void RemoveVirtualFolder(string name, bool refreshLibrary) - { - if (string.IsNullOrWhiteSpace(name)) - { - throw new ArgumentNullException("name"); - } - - var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; - - var path = Path.Combine(rootFolderPath, name); - - if (!_fileSystem.DirectoryExists(path)) - { - throw new DirectoryNotFoundException("The media folder does not exist"); - } - - _libraryMonitorFactory().Stop(); - - try - { - _fileSystem.DeleteDirectory(path, true); - } - finally - { - Task.Run(() => - { - // No need to start if scanning the library because it will handle it - if (refreshLibrary) - { - ValidateMediaLibrary(new Progress<double>(), CancellationToken.None); - } - else - { - // Need to add a delay here or directory watchers may still pick up the changes - var task = Task.Delay(1000); - // Have to block here to allow exceptions to bubble - Task.WaitAll(task); - - _libraryMonitorFactory().Start(); - } - }); - } - } - - private void RemoveContentTypeOverrides(string path) - { - if (string.IsNullOrWhiteSpace(path)) - { - throw new ArgumentNullException("path"); - } - - var removeList = new List<NameValuePair>(); - - foreach (var contentType in ConfigurationManager.Configuration.ContentTypes) - { - if (string.Equals(path, contentType.Name, StringComparison.OrdinalIgnoreCase) - || _fileSystem.ContainsSubPath(path, contentType.Name)) - { - removeList.Add(contentType); - } - } - - if (removeList.Count > 0) - { - ConfigurationManager.Configuration.ContentTypes = ConfigurationManager.Configuration.ContentTypes - .Except(removeList) - .ToArray(); - - ConfigurationManager.SaveConfiguration(); - } - } - - public void RemoveMediaPath(string virtualFolderName, string mediaPath) - { - if (string.IsNullOrWhiteSpace(mediaPath)) - { - throw new ArgumentNullException("mediaPath"); - } - - var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; - var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName); - - if (!_fileSystem.DirectoryExists(virtualFolderPath)) - { - throw new DirectoryNotFoundException(string.Format("The media collection {0} does not exist", virtualFolderName)); - } - - var shortcut = Directory.EnumerateFiles(virtualFolderPath, ShortcutFileSearch, SearchOption.AllDirectories).FirstOrDefault(f => _fileSystem.ResolveShortcut(f).Equals(mediaPath, StringComparison.OrdinalIgnoreCase)); - - if (!string.IsNullOrEmpty(shortcut)) - { - _fileSystem.DeleteFile(shortcut); - } - - var libraryOptions = CollectionFolder.GetLibraryOptions(virtualFolderPath); - - libraryOptions.PathInfos = libraryOptions - .PathInfos - .Where(i => !string.Equals(i.Path, mediaPath, StringComparison.Ordinal)) - .ToArray(); - - CollectionFolder.SaveLibraryOptions(virtualFolderPath, libraryOptions); - } - } -}
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Library/LocalTrailerPostScanTask.cs b/MediaBrowser.Server.Implementations/Library/LocalTrailerPostScanTask.cs deleted file mode 100644 index 78107b82d..000000000 --- a/MediaBrowser.Server.Implementations/Library/LocalTrailerPostScanTask.cs +++ /dev/null @@ -1,102 +0,0 @@ -using MediaBrowser.Controller.Channels; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Entities; -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Controller.Entities.TV; - -namespace MediaBrowser.Server.Implementations.Library -{ - public class LocalTrailerPostScanTask : ILibraryPostScanTask - { - private readonly ILibraryManager _libraryManager; - private readonly IChannelManager _channelManager; - - public LocalTrailerPostScanTask(ILibraryManager libraryManager, IChannelManager channelManager) - { - _libraryManager = libraryManager; - _channelManager = channelManager; - } - - public async Task Run(IProgress<double> progress, CancellationToken cancellationToken) - { - var items = _libraryManager.GetItemList(new InternalItemsQuery - { - IncludeItemTypes = new[] { typeof(BoxSet).Name, typeof(Game).Name, typeof(Movie).Name, typeof(Series).Name }, - Recursive = true - - }).OfType<IHasTrailers>().ToList(); - - var trailerTypes = Enum.GetNames(typeof(TrailerType)) - .Select(i => (TrailerType)Enum.Parse(typeof(TrailerType), i, true)) - .Except(new[] { TrailerType.LocalTrailer }) - .ToArray(); - - var trailers = _libraryManager.GetItemList(new InternalItemsQuery - { - IncludeItemTypes = new[] { typeof(Trailer).Name }, - TrailerTypes = trailerTypes, - Recursive = true - - }).ToArray(); - - var numComplete = 0; - - foreach (var item in items) - { - cancellationToken.ThrowIfCancellationRequested(); - - await AssignTrailers(item, trailers).ConfigureAwait(false); - - numComplete++; - double percent = numComplete; - percent /= items.Count; - progress.Report(percent * 100); - } - - progress.Report(100); - } - - private async Task AssignTrailers(IHasTrailers item, BaseItem[] channelTrailers) - { - if (item is Game) - { - return; - } - - var imdbId = item.GetProviderId(MetadataProviders.Imdb); - var tmdbId = item.GetProviderId(MetadataProviders.Tmdb); - - var trailers = channelTrailers.Where(i => - { - if (!string.IsNullOrWhiteSpace(imdbId) && - string.Equals(imdbId, i.GetProviderId(MetadataProviders.Imdb), StringComparison.OrdinalIgnoreCase)) - { - return true; - } - if (!string.IsNullOrWhiteSpace(tmdbId) && - string.Equals(tmdbId, i.GetProviderId(MetadataProviders.Tmdb), StringComparison.OrdinalIgnoreCase)) - { - return true; - } - return false; - }); - - var trailerIds = trailers.Select(i => i.Id) - .ToList(); - - if (!trailerIds.SequenceEqual(item.RemoteTrailerIds)) - { - item.RemoteTrailerIds = trailerIds; - - var baseItem = (BaseItem)item; - await baseItem.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None) - .ConfigureAwait(false); - } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs deleted file mode 100644 index 0f1931dda..000000000 --- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs +++ /dev/null @@ -1,648 +0,0 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Controller.Persistence; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.MediaInfo; -using MediaBrowser.Model.Serialization; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.IO; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Configuration; - -namespace MediaBrowser.Server.Implementations.Library -{ - public class MediaSourceManager : IMediaSourceManager, IDisposable - { - private readonly IItemRepository _itemRepo; - private readonly IUserManager _userManager; - private readonly ILibraryManager _libraryManager; - private readonly IJsonSerializer _jsonSerializer; - private readonly IFileSystem _fileSystem; - - private IMediaSourceProvider[] _providers; - private readonly ILogger _logger; - private readonly IUserDataManager _userDataManager; - - public MediaSourceManager(IItemRepository itemRepo, IUserManager userManager, ILibraryManager libraryManager, ILogger logger, IJsonSerializer jsonSerializer, IFileSystem fileSystem, IUserDataManager userDataManager) - { - _itemRepo = itemRepo; - _userManager = userManager; - _libraryManager = libraryManager; - _logger = logger; - _jsonSerializer = jsonSerializer; - _fileSystem = fileSystem; - _userDataManager = userDataManager; - } - - public void AddParts(IEnumerable<IMediaSourceProvider> providers) - { - _providers = providers.ToArray(); - } - - public IEnumerable<MediaStream> GetMediaStreams(MediaStreamQuery query) - { - var list = _itemRepo.GetMediaStreams(query) - .ToList(); - - foreach (var stream in list) - { - stream.SupportsExternalStream = StreamSupportsExternalStream(stream); - } - - return list; - } - - private bool StreamSupportsExternalStream(MediaStream stream) - { - if (stream.IsExternal) - { - return true; - } - - if (stream.IsTextSubtitleStream) - { - return true; - } - - return false; - } - - public IEnumerable<MediaStream> GetMediaStreams(string mediaSourceId) - { - var list = GetMediaStreams(new MediaStreamQuery - { - ItemId = new Guid(mediaSourceId) - }); - - return GetMediaStreamsForItem(list); - } - - public IEnumerable<MediaStream> GetMediaStreams(Guid itemId) - { - var list = GetMediaStreams(new MediaStreamQuery - { - ItemId = itemId - }); - - return GetMediaStreamsForItem(list); - } - - private IEnumerable<MediaStream> GetMediaStreamsForItem(IEnumerable<MediaStream> streams) - { - var list = streams.ToList(); - - var subtitleStreams = list - .Where(i => i.Type == MediaStreamType.Subtitle) - .ToList(); - - if (subtitleStreams.Count > 0) - { - foreach (var subStream in subtitleStreams) - { - subStream.SupportsExternalStream = StreamSupportsExternalStream(subStream); - } - } - - return list; - } - - public async Task<IEnumerable<MediaSourceInfo>> GetPlayackMediaSources(string id, string userId, bool enablePathSubstitution, string[] supportedLiveMediaTypes, CancellationToken cancellationToken) - { - var item = _libraryManager.GetItemById(id); - - var hasMediaSources = (IHasMediaSources)item; - User user = null; - - if (!string.IsNullOrWhiteSpace(userId)) - { - user = _userManager.GetUserById(userId); - } - - var mediaSources = GetStaticMediaSources(hasMediaSources, enablePathSubstitution, user); - var dynamicMediaSources = await GetDynamicMediaSources(hasMediaSources, cancellationToken).ConfigureAwait(false); - - var list = new List<MediaSourceInfo>(); - - list.AddRange(mediaSources); - - foreach (var source in dynamicMediaSources) - { - if (user != null) - { - SetUserProperties(hasMediaSources, source, user); - } - if (source.Protocol == MediaProtocol.File) - { - // TODO: Path substitution - if (!_fileSystem.FileExists(source.Path)) - { - source.SupportsDirectStream = false; - } - } - else if (source.Protocol == MediaProtocol.Http) - { - // TODO: Allow this when the source is plain http, e.g. not HLS or Mpeg Dash - source.SupportsDirectStream = false; - } - else - { - source.SupportsDirectStream = false; - } - - list.Add(source); - } - - foreach (var source in list) - { - if (user != null) - { - if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)) - { - if (!user.Policy.EnableAudioPlaybackTranscoding) - { - source.SupportsTranscoding = false; - } - } - } - } - - return SortMediaSources(list).Where(i => i.Type != MediaSourceType.Placeholder); - } - - private async Task<IEnumerable<MediaSourceInfo>> GetDynamicMediaSources(IHasMediaSources item, CancellationToken cancellationToken) - { - var tasks = _providers.Select(i => GetDynamicMediaSources(item, i, cancellationToken)); - var results = await Task.WhenAll(tasks).ConfigureAwait(false); - - return results.SelectMany(i => i.ToList()); - } - - private async Task<IEnumerable<MediaSourceInfo>> GetDynamicMediaSources(IHasMediaSources item, IMediaSourceProvider provider, CancellationToken cancellationToken) - { - try - { - var sources = await provider.GetMediaSources(item, cancellationToken).ConfigureAwait(false); - var list = sources.ToList(); - - foreach (var mediaSource in list) - { - SetKeyProperties(provider, mediaSource); - } - - return list; - } - catch (Exception ex) - { - _logger.ErrorException("Error getting media sources", ex); - return new List<MediaSourceInfo>(); - } - } - - private void SetKeyProperties(IMediaSourceProvider provider, MediaSourceInfo mediaSource) - { - var prefix = provider.GetType().FullName.GetMD5().ToString("N") + LiveStreamIdDelimeter; - - if (!string.IsNullOrWhiteSpace(mediaSource.OpenToken) && !mediaSource.OpenToken.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) - { - mediaSource.OpenToken = prefix + mediaSource.OpenToken; - } - - if (!string.IsNullOrWhiteSpace(mediaSource.LiveStreamId) && !mediaSource.LiveStreamId.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) - { - mediaSource.LiveStreamId = prefix + mediaSource.LiveStreamId; - } - } - - public async Task<MediaSourceInfo> GetMediaSource(IHasMediaSources item, string mediaSourceId, string liveStreamId, bool enablePathSubstitution, CancellationToken cancellationToken) - { - if (!string.IsNullOrWhiteSpace(liveStreamId)) - { - return await GetLiveStream(liveStreamId, cancellationToken).ConfigureAwait(false); - } - //await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - - //try - //{ - // var stream = _openStreams.Values.FirstOrDefault(i => string.Equals(i.MediaSource.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); - - // if (stream != null) - // { - // return stream.MediaSource; - // } - //} - //finally - //{ - // _liveStreamSemaphore.Release(); - //} - - var sources = await GetPlayackMediaSources(item.Id.ToString("N"), null, enablePathSubstitution, new[] { MediaType.Audio, MediaType.Video }, - CancellationToken.None).ConfigureAwait(false); - - return sources.FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); - } - - public IEnumerable<MediaSourceInfo> GetStaticMediaSources(IHasMediaSources item, bool enablePathSubstitution, User user = null) - { - if (item == null) - { - throw new ArgumentNullException("item"); - } - - if (!(item is Video)) - { - return item.GetMediaSources(enablePathSubstitution); - } - - var sources = item.GetMediaSources(enablePathSubstitution).ToList(); - - if (user != null) - { - foreach (var source in sources) - { - SetUserProperties(item, source, user); - } - } - - return sources; - } - - private void SetUserProperties(IHasUserData item, MediaSourceInfo source, User user) - { - var userData = item == null ? new UserItemData() : _userDataManager.GetUserData(user, item); - - var allowRememberingSelection = item == null || item.EnableRememberingTrackSelections; - - SetDefaultAudioStreamIndex(source, userData, user, allowRememberingSelection); - SetDefaultSubtitleStreamIndex(source, userData, user, allowRememberingSelection); - } - - private void SetDefaultSubtitleStreamIndex(MediaSourceInfo source, UserItemData userData, User user, bool allowRememberingSelection) - { - if (userData.SubtitleStreamIndex.HasValue && user.Configuration.RememberSubtitleSelections && user.Configuration.SubtitleMode != SubtitlePlaybackMode.None && allowRememberingSelection) - { - var index = userData.SubtitleStreamIndex.Value; - // Make sure the saved index is still valid - if (index == -1 || source.MediaStreams.Any(i => i.Type == MediaStreamType.Subtitle && i.Index == index)) - { - source.DefaultSubtitleStreamIndex = index; - return; - } - } - - var preferredSubs = string.IsNullOrEmpty(user.Configuration.SubtitleLanguagePreference) - ? new List<string>() : new List<string> { user.Configuration.SubtitleLanguagePreference }; - - var defaultAudioIndex = source.DefaultAudioStreamIndex; - var audioLangage = defaultAudioIndex == null - ? null - : source.MediaStreams.Where(i => i.Type == MediaStreamType.Audio && i.Index == defaultAudioIndex).Select(i => i.Language).FirstOrDefault(); - - source.DefaultSubtitleStreamIndex = MediaStreamSelector.GetDefaultSubtitleStreamIndex(source.MediaStreams, - preferredSubs, - user.Configuration.SubtitleMode, - audioLangage); - - MediaStreamSelector.SetSubtitleStreamScores(source.MediaStreams, preferredSubs, - user.Configuration.SubtitleMode, audioLangage); - } - - private void SetDefaultAudioStreamIndex(MediaSourceInfo source, UserItemData userData, User user, bool allowRememberingSelection) - { - if (userData.AudioStreamIndex.HasValue && user.Configuration.RememberAudioSelections && allowRememberingSelection) - { - var index = userData.AudioStreamIndex.Value; - // Make sure the saved index is still valid - if (source.MediaStreams.Any(i => i.Type == MediaStreamType.Audio && i.Index == index)) - { - source.DefaultAudioStreamIndex = index; - return; - } - } - - var preferredAudio = string.IsNullOrEmpty(user.Configuration.AudioLanguagePreference) - ? new string[] { } - : new[] { user.Configuration.AudioLanguagePreference }; - - source.DefaultAudioStreamIndex = MediaStreamSelector.GetDefaultAudioStreamIndex(source.MediaStreams, preferredAudio, user.Configuration.PlayDefaultAudioTrack); - } - - private IEnumerable<MediaSourceInfo> SortMediaSources(IEnumerable<MediaSourceInfo> sources) - { - return sources.OrderBy(i => - { - if (i.VideoType.HasValue && i.VideoType.Value == VideoType.VideoFile) - { - return 0; - } - - return 1; - - }).ThenBy(i => i.Video3DFormat.HasValue ? 1 : 0) - .ThenByDescending(i => - { - var stream = i.VideoStream; - - return stream == null || stream.Width == null ? 0 : stream.Width.Value; - }) - .ToList(); - } - - private readonly Dictionary<string, LiveStreamInfo> _openStreams = new Dictionary<string, LiveStreamInfo>(StringComparer.OrdinalIgnoreCase); - private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1); - - public async Task<LiveStreamResponse> OpenLiveStream(LiveStreamRequest request, bool enableAutoClose, CancellationToken cancellationToken) - { - await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - - try - { - var tuple = GetProvider(request.OpenToken); - var provider = tuple.Item1; - - var mediaSourceTuple = await provider.OpenMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false); - - var mediaSource = mediaSourceTuple.Item1; - - if (string.IsNullOrWhiteSpace(mediaSource.LiveStreamId)) - { - throw new InvalidOperationException(string.Format("{0} returned null LiveStreamId", provider.GetType().Name)); - } - - SetKeyProperties(provider, mediaSource); - - var info = new LiveStreamInfo - { - Date = DateTime.UtcNow, - EnableCloseTimer = enableAutoClose, - Id = mediaSource.LiveStreamId, - MediaSource = mediaSource, - DirectStreamProvider = mediaSourceTuple.Item2 - }; - - _openStreams[mediaSource.LiveStreamId] = info; - - if (enableAutoClose) - { - StartCloseTimer(); - } - - var json = _jsonSerializer.SerializeToString(mediaSource); - _logger.Debug("Live stream opened: " + json); - var clone = _jsonSerializer.DeserializeFromString<MediaSourceInfo>(json); - - if (!string.IsNullOrWhiteSpace(request.UserId)) - { - var user = _userManager.GetUserById(request.UserId); - var item = string.IsNullOrWhiteSpace(request.ItemId) - ? null - : _libraryManager.GetItemById(request.ItemId); - SetUserProperties(item, clone, user); - } - - return new LiveStreamResponse - { - MediaSource = clone - }; - } - finally - { - _liveStreamSemaphore.Release(); - } - } - - public async Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetLiveStreamWithDirectStreamProvider(string id, CancellationToken cancellationToken) - { - if (string.IsNullOrWhiteSpace(id)) - { - throw new ArgumentNullException("id"); - } - - _logger.Debug("Getting already opened live stream {0}", id); - - await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - - try - { - LiveStreamInfo info; - if (_openStreams.TryGetValue(id, out info)) - { - return new Tuple<MediaSourceInfo, IDirectStreamProvider>(info.MediaSource, info.DirectStreamProvider); - } - else - { - throw new ResourceNotFoundException(); - } - } - finally - { - _liveStreamSemaphore.Release(); - } - } - - public async Task<MediaSourceInfo> GetLiveStream(string id, CancellationToken cancellationToken) - { - var result = await GetLiveStreamWithDirectStreamProvider(id, cancellationToken).ConfigureAwait(false); - return result.Item1; - } - - public async Task PingLiveStream(string id, CancellationToken cancellationToken) - { - await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - - try - { - LiveStreamInfo info; - if (_openStreams.TryGetValue(id, out info)) - { - info.Date = DateTime.UtcNow; - } - else - { - _logger.Error("Failed to ping live stream {0}", id); - } - } - finally - { - _liveStreamSemaphore.Release(); - } - } - - private async Task CloseLiveStreamWithProvider(IMediaSourceProvider provider, string streamId) - { - _logger.Info("Closing live stream {0} with provider {1}", streamId, provider.GetType().Name); - - try - { - await provider.CloseMediaSource(streamId).ConfigureAwait(false); - } - catch (NotImplementedException) - { - } - catch (Exception ex) - { - _logger.ErrorException("Error closing live stream {0}", ex, streamId); - } - } - - public async Task CloseLiveStream(string id) - { - if (string.IsNullOrWhiteSpace(id)) - { - throw new ArgumentNullException("id"); - } - - await _liveStreamSemaphore.WaitAsync().ConfigureAwait(false); - - try - { - LiveStreamInfo current; - - if (_openStreams.TryGetValue(id, out current)) - { - _openStreams.Remove(id); - current.Closed = true; - - if (current.MediaSource.RequiresClosing) - { - var tuple = GetProvider(id); - - await CloseLiveStreamWithProvider(tuple.Item1, tuple.Item2).ConfigureAwait(false); - } - - if (_openStreams.Count == 0) - { - StopCloseTimer(); - } - } - } - finally - { - _liveStreamSemaphore.Release(); - } - } - - // Do not use a pipe here because Roku http requests to the server will fail, without any explicit error message. - private const char LiveStreamIdDelimeter = '_'; - - private Tuple<IMediaSourceProvider, string> GetProvider(string key) - { - if (string.IsNullOrWhiteSpace(key)) - { - throw new ArgumentException("key"); - } - - var keys = key.Split(new[] { LiveStreamIdDelimeter }, 2); - - var provider = _providers.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N"), keys[0], StringComparison.OrdinalIgnoreCase)); - - var splitIndex = key.IndexOf(LiveStreamIdDelimeter); - var keyId = key.Substring(splitIndex + 1); - - return new Tuple<IMediaSourceProvider, string>(provider, keyId); - } - - private Timer _closeTimer; - private readonly TimeSpan _openStreamMaxAge = TimeSpan.FromSeconds(180); - - private void StartCloseTimer() - { - StopCloseTimer(); - - _closeTimer = new Timer(CloseTimerCallback, null, _openStreamMaxAge, _openStreamMaxAge); - } - - private void StopCloseTimer() - { - var timer = _closeTimer; - - if (timer != null) - { - _closeTimer = null; - timer.Dispose(); - } - } - - private async void CloseTimerCallback(object state) - { - List<LiveStreamInfo> infos; - await _liveStreamSemaphore.WaitAsync().ConfigureAwait(false); - - try - { - infos = _openStreams - .Values - .Where(i => i.EnableCloseTimer && DateTime.UtcNow - i.Date > _openStreamMaxAge) - .ToList(); - } - finally - { - _liveStreamSemaphore.Release(); - } - - foreach (var info in infos) - { - if (!info.Closed) - { - try - { - await CloseLiveStream(info.Id).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.ErrorException("Error closing media source", ex); - } - } - } - } - - /// <summary> - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// </summary> - public void Dispose() - { - StopCloseTimer(); - Dispose(true); - } - - private readonly object _disposeLock = new object(); - /// <summary> - /// Releases unmanaged and - optionally - managed resources. - /// </summary> - /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> - protected virtual void Dispose(bool dispose) - { - if (dispose) - { - lock (_disposeLock) - { - foreach (var key in _openStreams.Keys.ToList()) - { - var task = CloseLiveStream(key); - - Task.WaitAll(task); - } - } - } - } - - private class LiveStreamInfo - { - public DateTime Date; - public bool EnableCloseTimer; - public string Id; - public bool Closed; - public MediaSourceInfo MediaSource; - public IDirectStreamProvider DirectStreamProvider; - } - } -}
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Library/MusicManager.cs b/MediaBrowser.Server.Implementations/Library/MusicManager.cs deleted file mode 100644 index 3ff434898..000000000 --- a/MediaBrowser.Server.Implementations/Library/MusicManager.cs +++ /dev/null @@ -1,157 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Playlists; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace MediaBrowser.Server.Implementations.Library -{ - public class MusicManager : IMusicManager - { - private readonly ILibraryManager _libraryManager; - - public MusicManager(ILibraryManager libraryManager) - { - _libraryManager = libraryManager; - } - - public IEnumerable<Audio> GetInstantMixFromSong(Audio item, User user) - { - var list = new List<Audio> - { - item - }; - - return list.Concat(GetInstantMixFromGenres(item.Genres, user)); - } - - public IEnumerable<Audio> GetInstantMixFromArtist(MusicArtist artist, User user) - { - var genres = user.RootFolder - .GetRecursiveChildren(user, new InternalItemsQuery(user) - { - IncludeItemTypes = new[] { typeof(Audio).Name } - }) - .Cast<Audio>() - .Where(i => i.HasAnyArtist(artist.Name)) - .SelectMany(i => i.Genres) - .Concat(artist.Genres) - .Distinct(StringComparer.OrdinalIgnoreCase); - - return GetInstantMixFromGenres(genres, user); - } - - public IEnumerable<Audio> GetInstantMixFromAlbum(MusicAlbum item, User user) - { - var genres = item - .GetRecursiveChildren(user, new InternalItemsQuery(user) - { - IncludeItemTypes = new[] { typeof(Audio).Name } - }) - .Cast<Audio>() - .SelectMany(i => i.Genres) - .Concat(item.Genres) - .DistinctNames(); - - return GetInstantMixFromGenres(genres, user); - } - - public IEnumerable<Audio> GetInstantMixFromFolder(Folder item, User user) - { - var genres = item - .GetRecursiveChildren(user, new InternalItemsQuery(user) - { - IncludeItemTypes = new[] {typeof(Audio).Name} - }) - .Cast<Audio>() - .SelectMany(i => i.Genres) - .Concat(item.Genres) - .DistinctNames(); - - return GetInstantMixFromGenres(genres, user); - } - - public IEnumerable<Audio> GetInstantMixFromPlaylist(Playlist item, User user) - { - var genres = item - .GetRecursiveChildren(user, new InternalItemsQuery(user) - { - IncludeItemTypes = new[] { typeof(Audio).Name } - }) - .Cast<Audio>() - .SelectMany(i => i.Genres) - .Concat(item.Genres) - .DistinctNames(); - - return GetInstantMixFromGenres(genres, user); - } - - public IEnumerable<Audio> GetInstantMixFromGenres(IEnumerable<string> genres, User user) - { - var genreList = genres.ToList(); - - var inputItems = _libraryManager.GetItemList(new InternalItemsQuery(user) - { - IncludeItemTypes = new[] { typeof(Audio).Name }, - - Genres = genreList.ToArray() - - }); - - var genresDictionary = genreList.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); - - return inputItems - .Cast<Audio>() - .Select(i => new Tuple<Audio, int>(i, i.Genres.Count(genresDictionary.ContainsKey))) - .Where(i => i.Item2 > 0) - .OrderByDescending(i => i.Item2) - .ThenBy(i => Guid.NewGuid()) - .Select(i => i.Item1) - .Take(100) - .OrderBy(i => Guid.NewGuid()); - } - - public IEnumerable<Audio> GetInstantMixFromItem(BaseItem item, User user) - { - var genre = item as MusicGenre; - if (genre != null) - { - return GetInstantMixFromGenres(new[] { item.Name }, user); - } - - var playlist = item as Playlist; - if (playlist != null) - { - return GetInstantMixFromPlaylist(playlist, user); - } - - var album = item as MusicAlbum; - if (album != null) - { - return GetInstantMixFromAlbum(album, user); - } - - var artist = item as MusicArtist; - if (artist != null) - { - return GetInstantMixFromArtist(artist, user); - } - - var song = item as Audio; - if (song != null) - { - return GetInstantMixFromSong(song, user); - } - - var folder = item as Folder; - if (folder != null) - { - return GetInstantMixFromFolder(folder, user); - } - - return new Audio[] { }; - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/PathExtensions.cs b/MediaBrowser.Server.Implementations/Library/PathExtensions.cs deleted file mode 100644 index 6c0e3237e..000000000 --- a/MediaBrowser.Server.Implementations/Library/PathExtensions.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Text.RegularExpressions; - -namespace MediaBrowser.Server.Implementations.Library -{ - public static class PathExtensions - { - /// <summary> - /// Gets the attribute value. - /// </summary> - /// <param name="str">The STR.</param> - /// <param name="attrib">The attrib.</param> - /// <returns>System.String.</returns> - /// <exception cref="System.ArgumentNullException">attrib</exception> - public static string GetAttributeValue(this string str, string attrib) - { - if (string.IsNullOrEmpty(str)) - { - throw new ArgumentNullException("str"); - } - - if (string.IsNullOrEmpty(attrib)) - { - throw new ArgumentNullException("attrib"); - } - - string srch = "[" + attrib + "="; - int start = str.IndexOf(srch, StringComparison.OrdinalIgnoreCase); - if (start > -1) - { - start += srch.Length; - int end = str.IndexOf(']', start); - return str.Substring(start, end - start); - } - // for imdbid we also accept pattern matching - if (string.Equals(attrib, "imdbid", StringComparison.OrdinalIgnoreCase)) - { - var m = Regex.Match(str, "tt\\d{7}", RegexOptions.IgnoreCase); - return m.Success ? m.Value : null; - } - - return null; - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs b/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs deleted file mode 100644 index c7d9d39b8..000000000 --- a/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs +++ /dev/null @@ -1,183 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Providers; -using System; -using System.IO; -using System.Linq; -using System.Text.RegularExpressions; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.IO; -using MediaBrowser.Model.IO; - -namespace MediaBrowser.Server.Implementations.Library -{ - /// <summary> - /// Class ResolverHelper - /// </summary> - public static class ResolverHelper - { - /// <summary> - /// Sets the initial item values. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="parent">The parent.</param> - /// <param name="fileSystem">The file system.</param> - /// <param name="libraryManager">The library manager.</param> - /// <param name="directoryService">The directory service.</param> - /// <exception cref="System.ArgumentException">Item must have a path</exception> - public static void SetInitialItemValues(BaseItem item, Folder parent, IFileSystem fileSystem, ILibraryManager libraryManager, IDirectoryService directoryService) - { - // This version of the below method has no ItemResolveArgs, so we have to require the path already being set - if (string.IsNullOrWhiteSpace(item.Path)) - { - throw new ArgumentException("Item must have a Path"); - } - - // If the resolver didn't specify this - if (parent != null) - { - item.SetParent(parent); - } - - item.Id = libraryManager.GetNewItemId(item.Path, item.GetType()); - - item.IsLocked = item.Path.IndexOf("[dontfetchmeta]", StringComparison.OrdinalIgnoreCase) != -1 || - item.GetParents().Any(i => i.IsLocked); - - // Make sure DateCreated and DateModified have values - var fileInfo = directoryService.GetFile(item.Path); - SetDateCreated(item, fileSystem, fileInfo); - - EnsureName(item, fileInfo); - } - - /// <summary> - /// Sets the initial item values. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="args">The args.</param> - /// <param name="fileSystem">The file system.</param> - /// <param name="libraryManager">The library manager.</param> - public static void SetInitialItemValues(BaseItem item, ItemResolveArgs args, IFileSystem fileSystem, ILibraryManager libraryManager) - { - // If the resolver didn't specify this - if (string.IsNullOrEmpty(item.Path)) - { - item.Path = args.Path; - } - - // If the resolver didn't specify this - if (args.Parent != null) - { - item.SetParent(args.Parent); - } - - item.Id = libraryManager.GetNewItemId(item.Path, item.GetType()); - - // Make sure the item has a name - EnsureName(item, args.FileInfo); - - item.IsLocked = item.Path.IndexOf("[dontfetchmeta]", StringComparison.OrdinalIgnoreCase) != -1 || - item.GetParents().Any(i => i.IsLocked); - - // Make sure DateCreated and DateModified have values - EnsureDates(fileSystem, item, args); - } - - /// <summary> - /// Ensures the name. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="fileInfo">The file information.</param> - private static void EnsureName(BaseItem item, FileSystemMetadata fileInfo) - { - // If the subclass didn't supply a name, add it here - if (string.IsNullOrEmpty(item.Name) && !string.IsNullOrEmpty(item.Path)) - { - item.Name = GetDisplayName(fileInfo.Name, fileInfo.IsDirectory); - } - } - - /// <summary> - /// Gets the display name. - /// </summary> - /// <param name="path">The path.</param> - /// <param name="isDirectory">if set to <c>true</c> [is directory].</param> - /// <returns>System.String.</returns> - private static string GetDisplayName(string path, bool isDirectory) - { - return isDirectory ? Path.GetFileName(path) : Path.GetFileNameWithoutExtension(path); - } - - /// <summary> - /// The MB name regex - /// </summary> - private static readonly Regex MbNameRegex = new Regex(@"(\[.*?\])", RegexOptions.Compiled); - - internal static string StripBrackets(string inputString) - { - var output = MbNameRegex.Replace(inputString, string.Empty).Trim(); - return Regex.Replace(output, @"\s+", " "); - } - - /// <summary> - /// Ensures DateCreated and DateModified have values - /// </summary> - /// <param name="fileSystem">The file system.</param> - /// <param name="item">The item.</param> - /// <param name="args">The args.</param> - private static void EnsureDates(IFileSystem fileSystem, BaseItem item, ItemResolveArgs args) - { - if (fileSystem == null) - { - throw new ArgumentNullException("fileSystem"); - } - if (item == null) - { - throw new ArgumentNullException("item"); - } - if (args == null) - { - throw new ArgumentNullException("args"); - } - - // See if a different path came out of the resolver than what went in - if (!string.Equals(args.Path, item.Path, StringComparison.OrdinalIgnoreCase)) - { - var childData = args.IsDirectory ? args.GetFileSystemEntryByPath(item.Path) : null; - - if (childData != null) - { - SetDateCreated(item, fileSystem, childData); - } - else - { - var fileData = fileSystem.GetFileSystemInfo(item.Path); - - if (fileData.Exists) - { - SetDateCreated(item, fileSystem, fileData); - } - } - } - else - { - SetDateCreated(item, fileSystem, args.FileInfo); - } - } - - private static void SetDateCreated(BaseItem item, IFileSystem fileSystem, FileSystemMetadata info) - { - var config = BaseItem.ConfigurationManager.GetMetadataConfiguration(); - - if (config.UseFileCreationTimeForDateAdded) - { - item.DateCreated = fileSystem.GetCreationTimeUtc(info); - } - else - { - item.DateCreated = DateTime.UtcNow; - } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs deleted file mode 100644 index 039a17100..000000000 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs +++ /dev/null @@ -1,68 +0,0 @@ -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Resolvers; -using MediaBrowser.Model.Entities; -using System; - -namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio -{ - /// <summary> - /// Class AudioResolver - /// </summary> - public class AudioResolver : ItemResolver<Controller.Entities.Audio.Audio> - { - private readonly ILibraryManager _libraryManager; - - public AudioResolver(ILibraryManager libraryManager) - { - _libraryManager = libraryManager; - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override ResolverPriority Priority - { - get { return ResolverPriority.Last; } - } - - /// <summary> - /// Resolves the specified args. - /// </summary> - /// <param name="args">The args.</param> - /// <returns>Entities.Audio.Audio.</returns> - protected override Controller.Entities.Audio.Audio Resolve(ItemResolveArgs args) - { - // Return audio if the path is a file and has a matching extension - - if (!args.IsDirectory) - { - var libraryOptions = args.GetLibraryOptions(); - - if (_libraryManager.IsAudioFile(args.Path, libraryOptions)) - { - var collectionType = args.GetCollectionType(); - - var isMixed = string.IsNullOrWhiteSpace(collectionType); - - // For conflicting extensions, give priority to videos - if (isMixed && _libraryManager.IsVideoFile(args.Path, libraryOptions)) - { - return null; - } - - var isStandalone = args.Parent == null; - - if (isStandalone || - string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase) || - isMixed) - { - return new Controller.Entities.Audio.Audio(); - } - } - } - - return null; - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs deleted file mode 100644 index c1ac7d68c..000000000 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs +++ /dev/null @@ -1,173 +0,0 @@ -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Controller.Resolvers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Naming.Audio; -using MediaBrowser.Server.Implementations.Logging; -using System; -using System.Collections.Generic; -using System.IO; -using MediaBrowser.Common.IO; -using MediaBrowser.Model.IO; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.IO; -using MediaBrowser.Model.Configuration; - -namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio -{ - /// <summary> - /// Class MusicAlbumResolver - /// </summary> - public class MusicAlbumResolver : ItemResolver<MusicAlbum> - { - private readonly ILogger _logger; - private readonly IFileSystem _fileSystem; - private readonly ILibraryManager _libraryManager; - - public MusicAlbumResolver(ILogger logger, IFileSystem fileSystem, ILibraryManager libraryManager) - { - _logger = logger; - _fileSystem = fileSystem; - _libraryManager = libraryManager; - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override ResolverPriority Priority - { - get - { - // Behind special folder resolver - return ResolverPriority.Second; - } - } - - /// <summary> - /// Resolves the specified args. - /// </summary> - /// <param name="args">The args.</param> - /// <returns>MusicAlbum.</returns> - protected override MusicAlbum Resolve(ItemResolveArgs args) - { - if (!args.IsDirectory) return null; - - // Avoid mis-identifying top folders - if (args.HasParent<MusicAlbum>()) return null; - if (args.Parent.IsRoot) return null; - - var collectionType = args.GetCollectionType(); - - var isMusicMediaFolder = string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase); - - // If there's a collection type and it's not music, don't allow it. - if (!isMusicMediaFolder) - { - return null; - } - - return IsMusicAlbum(args) ? new MusicAlbum() : null; - } - - - /// <summary> - /// Determine if the supplied file data points to a music album - /// </summary> - public bool IsMusicAlbum(string path, IDirectoryService directoryService, LibraryOptions libraryOptions) - { - return ContainsMusic(directoryService.GetFileSystemEntries(path), true, directoryService, _logger, _fileSystem, libraryOptions, _libraryManager); - } - - /// <summary> - /// Determine if the supplied resolve args should be considered a music album - /// </summary> - /// <param name="args">The args.</param> - /// <returns><c>true</c> if [is music album] [the specified args]; otherwise, <c>false</c>.</returns> - private bool IsMusicAlbum(ItemResolveArgs args) - { - // Args points to an album if parent is an Artist folder or it directly contains music - if (args.IsDirectory) - { - //if (args.Parent is MusicArtist) return true; //saves us from testing children twice - if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService, _logger, _fileSystem, args.GetLibraryOptions(), _libraryManager)) return true; - } - - return false; - } - - /// <summary> - /// Determine if the supplied list contains what we should consider music - /// </summary> - private bool ContainsMusic(IEnumerable<FileSystemMetadata> list, - bool allowSubfolders, - IDirectoryService directoryService, - ILogger logger, - IFileSystem fileSystem, - LibraryOptions libraryOptions, - ILibraryManager libraryManager) - { - var discSubfolderCount = 0; - var notMultiDisc = false; - - foreach (var fileSystemInfo in list) - { - if (fileSystemInfo.IsDirectory) - { - if (allowSubfolders) - { - var path = fileSystemInfo.FullName; - var isMultiDisc = IsMultiDiscFolder(path, libraryOptions); - - if (isMultiDisc) - { - var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryOptions, libraryManager); - - if (hasMusic) - { - logger.Debug("Found multi-disc folder: " + path); - discSubfolderCount++; - } - } - else - { - var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryOptions, libraryManager); - - if (hasMusic) - { - // If there are folders underneath with music that are not multidisc, then this can't be a multi-disc album - notMultiDisc = true; - } - } - } - } - - var fullName = fileSystemInfo.FullName; - - if (libraryManager.IsAudioFile(fullName, libraryOptions)) - { - return true; - } - } - - if (notMultiDisc) - { - return false; - } - - return discSubfolderCount > 0; - } - - private bool IsMultiDiscFolder(string path, LibraryOptions libraryOptions) - { - var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions(libraryOptions); - - var parser = new AlbumParser(namingOptions, new PatternsLogger()); - var result = parser.ParseMultiPart(path); - - return result.IsMultiPart; - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs deleted file mode 100644 index be651b9c8..000000000 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs +++ /dev/null @@ -1,94 +0,0 @@ -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Resolvers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using System; -using System.IO; -using System.Linq; -using MediaBrowser.Common.IO; -using MediaBrowser.Model.IO; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.IO; - -namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio -{ - /// <summary> - /// Class MusicArtistResolver - /// </summary> - public class MusicArtistResolver : ItemResolver<MusicArtist> - { - private readonly ILogger _logger; - private readonly IFileSystem _fileSystem; - private readonly ILibraryManager _libraryManager; - private readonly IServerConfigurationManager _config; - - public MusicArtistResolver(ILogger logger, IFileSystem fileSystem, ILibraryManager libraryManager, IServerConfigurationManager config) - { - _logger = logger; - _fileSystem = fileSystem; - _libraryManager = libraryManager; - _config = config; - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override ResolverPriority Priority - { - get - { - // Behind special folder resolver - return ResolverPriority.Second; - } - } - - /// <summary> - /// Resolves the specified args. - /// </summary> - /// <param name="args">The args.</param> - /// <returns>MusicArtist.</returns> - protected override MusicArtist Resolve(ItemResolveArgs args) - { - if (!args.IsDirectory) return null; - - // Don't allow nested artists - if (args.HasParent<MusicArtist>() || args.HasParent<MusicAlbum>()) - { - return null; - } - - var collectionType = args.GetCollectionType(); - - var isMusicMediaFolder = string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase); - - // If there's a collection type and it's not music, it can't be a series - if (!isMusicMediaFolder) - { - return null; - } - - if (args.ContainsFileSystemEntryByName("artist.nfo")) - { - return new MusicArtist(); - } - - if (_config.Configuration.EnableSimpleArtistDetection) - { - return null; - } - - // Avoid mis-identifying top folders - if (args.Parent.IsRoot) return null; - - var directoryService = args.DirectoryService; - - var albumResolver = new MusicAlbumResolver(_logger, _fileSystem, _libraryManager); - - // If we contain an album assume we are an artist folder - return args.FileSystemChildren.Where(i => i.IsDirectory).Any(i => albumResolver.IsMusicAlbum(i.FullName, directoryService, args.GetLibraryOptions())) ? new MusicArtist() : null; - } - - } -} diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs deleted file mode 100644 index 4dce16139..000000000 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs +++ /dev/null @@ -1,297 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Entities; -using MediaBrowser.Naming.Video; -using MediaBrowser.Server.Implementations.Logging; -using System; -using System.IO; - -namespace MediaBrowser.Server.Implementations.Library.Resolvers -{ - /// <summary> - /// Resolves a Path into a Video or Video subclass - /// </summary> - /// <typeparam name="T"></typeparam> - public abstract class BaseVideoResolver<T> : Controller.Resolvers.ItemResolver<T> - where T : Video, new() - { - protected readonly ILibraryManager LibraryManager; - - protected BaseVideoResolver(ILibraryManager libraryManager) - { - LibraryManager = libraryManager; - } - - /// <summary> - /// Resolves the specified args. - /// </summary> - /// <param name="args">The args.</param> - /// <returns>`0.</returns> - protected override T Resolve(ItemResolveArgs args) - { - return ResolveVideo<T>(args, false); - } - - /// <summary> - /// Resolves the video. - /// </summary> - /// <typeparam name="TVideoType">The type of the T video type.</typeparam> - /// <param name="args">The args.</param> - /// <param name="parseName">if set to <c>true</c> [parse name].</param> - /// <returns>``0.</returns> - protected TVideoType ResolveVideo<TVideoType>(ItemResolveArgs args, bool parseName) - where TVideoType : Video, new() - { - var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions(); - - // If the path is a file check for a matching extensions - var parser = new Naming.Video.VideoResolver(namingOptions, new PatternsLogger()); - - if (args.IsDirectory) - { - TVideoType video = null; - VideoFileInfo videoInfo = null; - - // Loop through each child file/folder and see if we find a video - foreach (var child in args.FileSystemChildren) - { - var filename = child.Name; - - if (child.IsDirectory) - { - if (IsDvdDirectory(filename)) - { - videoInfo = parser.ResolveDirectory(args.Path); - - if (videoInfo == null) - { - return null; - } - - video = new TVideoType - { - Path = args.Path, - VideoType = VideoType.Dvd, - ProductionYear = videoInfo.Year - }; - break; - } - if (IsBluRayDirectory(filename)) - { - videoInfo = parser.ResolveDirectory(args.Path); - - if (videoInfo == null) - { - return null; - } - - video = new TVideoType - { - Path = args.Path, - VideoType = VideoType.BluRay, - ProductionYear = videoInfo.Year - }; - break; - } - } - else if (IsDvdFile(filename)) - { - videoInfo = parser.ResolveDirectory(args.Path); - - if (videoInfo == null) - { - return null; - } - - video = new TVideoType - { - Path = args.Path, - VideoType = VideoType.Dvd, - ProductionYear = videoInfo.Year - }; - break; - } - } - - if (video != null) - { - video.Name = parseName ? - videoInfo.Name : - Path.GetFileName(args.Path); - - Set3DFormat(video, videoInfo); - } - - return video; - } - else - { - var videoInfo = parser.Resolve(args.Path, false, false); - - if (videoInfo == null) - { - return null; - } - - if (LibraryManager.IsVideoFile(args.Path, args.GetLibraryOptions()) || videoInfo.IsStub) - { - var path = args.Path; - - var video = new TVideoType - { - Path = path, - IsInMixedFolder = true, - ProductionYear = videoInfo.Year - }; - - SetVideoType(video, videoInfo); - - video.Name = parseName ? - videoInfo.Name : - Path.GetFileNameWithoutExtension(args.Path); - - Set3DFormat(video, videoInfo); - - return video; - } - } - - return null; - } - - protected void SetVideoType(Video video, VideoFileInfo videoInfo) - { - var extension = Path.GetExtension(video.Path); - video.VideoType = string.Equals(extension, ".iso", StringComparison.OrdinalIgnoreCase) || - string.Equals(extension, ".img", StringComparison.OrdinalIgnoreCase) ? - VideoType.Iso : - VideoType.VideoFile; - - video.IsShortcut = string.Equals(extension, ".strm", StringComparison.OrdinalIgnoreCase); - video.IsPlaceHolder = videoInfo.IsStub; - - if (videoInfo.IsStub) - { - if (string.Equals(videoInfo.StubType, "dvd", StringComparison.OrdinalIgnoreCase)) - { - video.VideoType = VideoType.Dvd; - } - else if (string.Equals(videoInfo.StubType, "hddvd", StringComparison.OrdinalIgnoreCase)) - { - video.VideoType = VideoType.HdDvd; - video.IsHD = true; - } - else if (string.Equals(videoInfo.StubType, "bluray", StringComparison.OrdinalIgnoreCase)) - { - video.VideoType = VideoType.BluRay; - video.IsHD = true; - } - else if (string.Equals(videoInfo.StubType, "hdtv", StringComparison.OrdinalIgnoreCase)) - { - video.IsHD = true; - } - } - - SetIsoType(video); - } - - protected void SetIsoType(Video video) - { - if (video.VideoType == VideoType.Iso) - { - if (video.Path.IndexOf("dvd", StringComparison.OrdinalIgnoreCase) != -1) - { - video.IsoType = IsoType.Dvd; - } - else if (video.Path.IndexOf("bluray", StringComparison.OrdinalIgnoreCase) != -1) - { - video.IsoType = IsoType.BluRay; - } - } - } - - protected void Set3DFormat(Video video, bool is3D, string format3D) - { - if (is3D) - { - if (string.Equals(format3D, "fsbs", StringComparison.OrdinalIgnoreCase)) - { - video.Video3DFormat = Video3DFormat.FullSideBySide; - } - else if (string.Equals(format3D, "ftab", StringComparison.OrdinalIgnoreCase)) - { - video.Video3DFormat = Video3DFormat.FullTopAndBottom; - } - else if (string.Equals(format3D, "hsbs", StringComparison.OrdinalIgnoreCase)) - { - video.Video3DFormat = Video3DFormat.HalfSideBySide; - } - else if (string.Equals(format3D, "htab", StringComparison.OrdinalIgnoreCase)) - { - video.Video3DFormat = Video3DFormat.HalfTopAndBottom; - } - else if (string.Equals(format3D, "sbs", StringComparison.OrdinalIgnoreCase)) - { - video.Video3DFormat = Video3DFormat.HalfSideBySide; - } - else if (string.Equals(format3D, "sbs3d", StringComparison.OrdinalIgnoreCase)) - { - video.Video3DFormat = Video3DFormat.HalfSideBySide; - } - else if (string.Equals(format3D, "tab", StringComparison.OrdinalIgnoreCase)) - { - video.Video3DFormat = Video3DFormat.HalfTopAndBottom; - } - else if (string.Equals(format3D, "mvc", StringComparison.OrdinalIgnoreCase)) - { - video.Video3DFormat = Video3DFormat.MVC; - } - } - } - - protected void Set3DFormat(Video video, VideoFileInfo videoInfo) - { - Set3DFormat(video, videoInfo.Is3D, videoInfo.Format3D); - } - - protected void Set3DFormat(Video video) - { - var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions(); - - var resolver = new Format3DParser(namingOptions, new PatternsLogger()); - var result = resolver.Parse(video.Path); - - Set3DFormat(video, result.Is3D, result.Format3D); - } - - /// <summary> - /// Determines whether [is DVD directory] [the specified directory name]. - /// </summary> - /// <param name="directoryName">Name of the directory.</param> - /// <returns><c>true</c> if [is DVD directory] [the specified directory name]; otherwise, <c>false</c>.</returns> - protected bool IsDvdDirectory(string directoryName) - { - return string.Equals(directoryName, "video_ts", StringComparison.OrdinalIgnoreCase); - } - - /// <summary> - /// Determines whether [is DVD file] [the specified name]. - /// </summary> - /// <param name="name">The name.</param> - /// <returns><c>true</c> if [is DVD file] [the specified name]; otherwise, <c>false</c>.</returns> - protected bool IsDvdFile(string name) - { - return string.Equals(name, "video_ts.ifo", StringComparison.OrdinalIgnoreCase); - } - - /// <summary> - /// Determines whether [is blu ray directory] [the specified directory name]. - /// </summary> - /// <param name="directoryName">Name of the directory.</param> - /// <returns><c>true</c> if [is blu ray directory] [the specified directory name]; otherwise, <c>false</c>.</returns> - protected bool IsBluRayDirectory(string directoryName) - { - return string.Equals(directoryName, "bdmv", StringComparison.OrdinalIgnoreCase); - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/FolderResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/FolderResolver.cs deleted file mode 100644 index ff07c5282..000000000 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/FolderResolver.cs +++ /dev/null @@ -1,56 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Resolvers; - -namespace MediaBrowser.Server.Implementations.Library.Resolvers -{ - /// <summary> - /// Class FolderResolver - /// </summary> - public class FolderResolver : FolderResolver<Folder> - { - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override ResolverPriority Priority - { - get { return ResolverPriority.Last; } - } - - /// <summary> - /// Resolves the specified args. - /// </summary> - /// <param name="args">The args.</param> - /// <returns>Folder.</returns> - protected override Folder Resolve(ItemResolveArgs args) - { - if (args.IsDirectory) - { - return new Folder(); - } - - return null; - } - } - - /// <summary> - /// Class FolderResolver - /// </summary> - /// <typeparam name="TItemType">The type of the T item type.</typeparam> - public abstract class FolderResolver<TItemType> : ItemResolver<TItemType> - where TItemType : Folder, new() - { - /// <summary> - /// Sets the initial item values. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="args">The args.</param> - protected override void SetInitialItemValues(TItemType item, ItemResolveArgs args) - { - base.SetInitialItemValues(item, args); - - item.IsRoot = args.Parent == null; - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/ItemResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/ItemResolver.cs deleted file mode 100644 index a03eda263..000000000 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/ItemResolver.cs +++ /dev/null @@ -1,62 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Resolvers; - -namespace MediaBrowser.Server.Implementations.Library.Resolvers -{ - /// <summary> - /// Class ItemResolver - /// </summary> - /// <typeparam name="T"></typeparam> - public abstract class ItemResolver<T> : IItemResolver - where T : BaseItem, new() - { - /// <summary> - /// Resolves the specified args. - /// </summary> - /// <param name="args">The args.</param> - /// <returns>`0.</returns> - protected virtual T Resolve(ItemResolveArgs args) - { - return null; - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public virtual ResolverPriority Priority - { - get - { - return ResolverPriority.First; - } - } - - /// <summary> - /// Sets initial values on the newly resolved item - /// </summary> - /// <param name="item">The item.</param> - /// <param name="args">The args.</param> - protected virtual void SetInitialItemValues(T item, ItemResolveArgs args) - { - } - - /// <summary> - /// Resolves the path. - /// </summary> - /// <param name="args">The args.</param> - /// <returns>BaseItem.</returns> - BaseItem IItemResolver.ResolvePath(ItemResolveArgs args) - { - var item = Resolve(args); - - if (item != null) - { - SetInitialItemValues(item, args); - } - - return item; - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs deleted file mode 100644 index e3447afc9..000000000 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs +++ /dev/null @@ -1,77 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Entities; -using System; -using System.IO; - -namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies -{ - /// <summary> - /// Class BoxSetResolver - /// </summary> - public class BoxSetResolver : FolderResolver<BoxSet> - { - /// <summary> - /// Resolves the specified args. - /// </summary> - /// <param name="args">The args.</param> - /// <returns>BoxSet.</returns> - protected override BoxSet Resolve(ItemResolveArgs args) - { - // It's a boxset if all of the following conditions are met: - // Is a Directory - // Contains [boxset] in the path - if (args.IsDirectory) - { - var filename = Path.GetFileName(args.Path); - - if (string.IsNullOrEmpty(filename)) - { - return null; - } - - if (filename.IndexOf("[boxset]", StringComparison.OrdinalIgnoreCase) != -1 || - args.ContainsFileSystemEntryByName("collection.xml")) - { - return new BoxSet - { - Path = args.Path, - Name = ResolverHelper.StripBrackets(Path.GetFileName(args.Path)) - }; - } - } - - return null; - } - - /// <summary> - /// Sets the initial item values. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="args">The args.</param> - protected override void SetInitialItemValues(BoxSet item, ItemResolveArgs args) - { - base.SetInitialItemValues(item, args); - - SetProviderIdFromPath(item); - } - - /// <summary> - /// Sets the provider id from path. - /// </summary> - /// <param name="item">The item.</param> - private void SetProviderIdFromPath(BaseItem item) - { - //we need to only look at the name of this actual item (not parents) - var justName = Path.GetFileName(item.Path); - - var id = justName.GetAttributeValue("tmdbid"); - - if (!string.IsNullOrEmpty(id)) - { - item.SetProviderId(MetadataProviders.Tmdb, id); - } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs deleted file mode 100644 index bb1d57688..000000000 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ /dev/null @@ -1,541 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Controller.Resolvers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Extensions; -using MediaBrowser.Naming.Video; -using MediaBrowser.Server.Implementations.Logging; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.IO; -using MediaBrowser.Model.IO; - -namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies -{ - /// <summary> - /// Class MovieResolver - /// </summary> - public class MovieResolver : BaseVideoResolver<Video>, IMultiItemResolver - { - public MovieResolver(ILibraryManager libraryManager) - : base(libraryManager) - { - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override ResolverPriority Priority - { - get - { - // Give plugins a chance to catch iso's first - // Also since we have to loop through child files looking for videos, - // see if we can avoid some of that by letting other resolvers claim folders first - // Also run after series resolver - return ResolverPriority.Third; - } - } - - public MultiItemResolverResult ResolveMultiple(Folder parent, - List<FileSystemMetadata> files, - string collectionType, - IDirectoryService directoryService) - { - var result = ResolveMultipleInternal(parent, files, collectionType, directoryService); - - if (result != null) - { - foreach (var item in result.Items) - { - SetInitialItemValues((Video)item, null); - } - } - - return result; - } - - private MultiItemResolverResult ResolveMultipleInternal(Folder parent, - List<FileSystemMetadata> files, - string collectionType, - IDirectoryService directoryService) - { - if (IsInvalid(parent, collectionType)) - { - return null; - } - - if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase)) - { - return ResolveVideos<MusicVideo>(parent, files, directoryService, false); - } - - if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) || - string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase)) - { - return ResolveVideos<Video>(parent, files, directoryService, false); - } - - if (string.IsNullOrEmpty(collectionType)) - { - // Owned items should just use the plain video type - if (parent == null) - { - return ResolveVideos<Video>(parent, files, directoryService, false); - } - - if (parent is Series || parent.GetParents().OfType<Series>().Any()) - { - return null; - } - - return ResolveVideos<Movie>(parent, files, directoryService, false); - } - - if (string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase)) - { - return ResolveVideos<Movie>(parent, files, directoryService, true); - } - - return null; - } - - private MultiItemResolverResult ResolveVideos<T>(Folder parent, IEnumerable<FileSystemMetadata> fileSystemEntries, IDirectoryService directoryService, bool suppportMultiEditions) - where T : Video, new() - { - var files = new List<FileSystemMetadata>(); - var videos = new List<BaseItem>(); - var leftOver = new List<FileSystemMetadata>(); - - // Loop through each child file/folder and see if we find a video - foreach (var child in fileSystemEntries) - { - if (child.IsDirectory) - { - leftOver.Add(child); - } - else if (IsIgnored(child.Name)) - { - - } - else - { - files.Add(child); - } - } - - var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions(); - - var resolver = new VideoListResolver(namingOptions, new PatternsLogger()); - var resolverResult = resolver.Resolve(files, suppportMultiEditions).ToList(); - - var result = new MultiItemResolverResult - { - ExtraFiles = leftOver, - Items = videos - }; - - var isInMixedFolder = resolverResult.Count > 1; - - foreach (var video in resolverResult) - { - var firstVideo = video.Files.First(); - - var videoItem = new T - { - Path = video.Files[0].Path, - IsInMixedFolder = isInMixedFolder, - ProductionYear = video.Year, - Name = video.Name, - AdditionalParts = video.Files.Skip(1).Select(i => i.Path).ToList(), - LocalAlternateVersions = video.AlternateVersions.Select(i => i.Path).ToList() - }; - - SetVideoType(videoItem, firstVideo); - Set3DFormat(videoItem, firstVideo); - - result.Items.Add(videoItem); - } - - result.ExtraFiles.AddRange(files.Where(i => !ContainsFile(resolverResult, i))); - - return result; - } - - private bool ContainsFile(List<VideoInfo> result, FileSystemMetadata file) - { - return result.Any(i => ContainsFile(i, file)); - } - - private bool ContainsFile(VideoInfo result, FileSystemMetadata file) - { - return result.Files.Any(i => ContainsFile(i, file)) || - result.AlternateVersions.Any(i => ContainsFile(i, file)) || - result.Extras.Any(i => ContainsFile(i, file)); - } - - private bool ContainsFile(VideoFileInfo result, FileSystemMetadata file) - { - return string.Equals(result.Path, file.FullName, StringComparison.OrdinalIgnoreCase); - } - - /// <summary> - /// Resolves the specified args. - /// </summary> - /// <param name="args">The args.</param> - /// <returns>Video.</returns> - protected override Video Resolve(ItemResolveArgs args) - { - var collectionType = args.GetCollectionType(); - - if (IsInvalid(args.Parent, collectionType)) - { - return null; - } - - // Find movies with their own folders - if (args.IsDirectory) - { - var files = args.FileSystemChildren - .Where(i => !LibraryManager.IgnoreFile(i, args.Parent)) - .ToList(); - - if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase)) - { - return FindMovie<MusicVideo>(args.Path, args.Parent, files, args.DirectoryService, collectionType, false); - } - - if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase)) - { - return FindMovie<Video>(args.Path, args.Parent, files, args.DirectoryService, collectionType, false); - } - - if (string.IsNullOrEmpty(collectionType)) - { - // Owned items will be caught by the plain video resolver - if (args.Parent == null) - { - //return FindMovie<Video>(args.Path, args.Parent, files, args.DirectoryService, collectionType); - return null; - } - - if (args.HasParent<Series>()) - { - return null; - } - - { - return FindMovie<Movie>(args.Path, args.Parent, files, args.DirectoryService, collectionType, true); - } - } - - if (string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase)) - { - return FindMovie<Movie>(args.Path, args.Parent, files, args.DirectoryService, collectionType, true); - } - - return null; - } - - // Owned items will be caught by the plain video resolver - if (args.Parent == null) - { - return null; - } - - Video item = null; - - if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase)) - { - item = ResolveVideo<MusicVideo>(args, false); - } - - // To find a movie file, the collection type must be movies or boxsets - else if (string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase)) - { - item = ResolveVideo<Movie>(args, true); - } - - else if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) || - string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase)) - { - item = ResolveVideo<Video>(args, false); - } - else if (string.IsNullOrEmpty(collectionType)) - { - if (args.HasParent<Series>()) - { - return null; - } - - item = ResolveVideo<Video>(args, false); - } - - if (item != null) - { - item.IsInMixedFolder = true; - } - - return item; - } - - private bool IsIgnored(string filename) - { - // Ignore samples - var sampleFilename = " " + filename.Replace(".", " ", StringComparison.OrdinalIgnoreCase) - .Replace("-", " ", StringComparison.OrdinalIgnoreCase) - .Replace("_", " ", StringComparison.OrdinalIgnoreCase) - .Replace("!", " ", StringComparison.OrdinalIgnoreCase); - - if (sampleFilename.IndexOf(" sample ", StringComparison.OrdinalIgnoreCase) != -1) - { - return true; - } - - return false; - } - - /// <summary> - /// Sets the initial item values. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="args">The args.</param> - protected override void SetInitialItemValues(Video item, ItemResolveArgs args) - { - base.SetInitialItemValues(item, args); - - SetProviderIdsFromPath(item); - } - - /// <summary> - /// Sets the provider id from path. - /// </summary> - /// <param name="item">The item.</param> - private void SetProviderIdsFromPath(Video item) - { - if (item is Movie || item is MusicVideo) - { - //we need to only look at the name of this actual item (not parents) - var justName = item.IsInMixedFolder ? Path.GetFileName(item.Path) : Path.GetFileName(item.ContainingFolderPath); - - if (!string.IsNullOrWhiteSpace(justName)) - { - // check for tmdb id - var tmdbid = justName.GetAttributeValue("tmdbid"); - - if (!string.IsNullOrWhiteSpace(tmdbid)) - { - item.SetProviderId(MetadataProviders.Tmdb, tmdbid); - } - } - - if (!string.IsNullOrWhiteSpace(item.Path)) - { - // check for imdb id - we use full media path, as we can assume, that this will match in any use case (wither id in parent dir or in file name) - var imdbid = item.Path.GetAttributeValue("imdbid"); - - if (!string.IsNullOrWhiteSpace(imdbid)) - { - item.SetProviderId(MetadataProviders.Imdb, imdbid); - } - } - } - } - - /// <summary> - /// Finds a movie based on a child file system entries - /// </summary> - /// <typeparam name="T"></typeparam> - /// <returns>Movie.</returns> - private T FindMovie<T>(string path, Folder parent, List<FileSystemMetadata> fileSystemEntries, IDirectoryService directoryService, string collectionType, bool allowFilesAsFolders) - where T : Video, new() - { - var multiDiscFolders = new List<FileSystemMetadata>(); - - // Search for a folder rip - foreach (var child in fileSystemEntries) - { - var filename = child.Name; - - if (child.IsDirectory) - { - if (IsDvdDirectory(filename)) - { - var movie = new T - { - Path = path, - VideoType = VideoType.Dvd - }; - Set3DFormat(movie); - return movie; - } - if (IsBluRayDirectory(filename)) - { - var movie = new T - { - Path = path, - VideoType = VideoType.BluRay - }; - Set3DFormat(movie); - return movie; - } - - multiDiscFolders.Add(child); - } - else if (IsDvdFile(filename)) - { - var movie = new T - { - Path = path, - VideoType = VideoType.Dvd - }; - Set3DFormat(movie); - return movie; - } - } - - if (allowFilesAsFolders) - { - // TODO: Allow GetMultiDiscMovie in here - var supportsMultiVersion = !string.Equals(collectionType, CollectionType.HomeVideos) && - !string.Equals(collectionType, CollectionType.Photos) && - !string.Equals(collectionType, CollectionType.MusicVideos); - - var result = ResolveVideos<T>(parent, fileSystemEntries, directoryService, supportsMultiVersion); - - if (result.Items.Count == 1) - { - var movie = (T)result.Items[0]; - movie.IsInMixedFolder = false; - movie.Name = Path.GetFileName(movie.ContainingFolderPath); - return movie; - } - - if (result.Items.Count == 0 && multiDiscFolders.Count > 0) - { - return GetMultiDiscMovie<T>(multiDiscFolders, directoryService); - } - } - - return null; - } - - /// <summary> - /// Gets the multi disc movie. - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="multiDiscFolders">The folders.</param> - /// <param name="directoryService">The directory service.</param> - /// <returns>``0.</returns> - private T GetMultiDiscMovie<T>(List<FileSystemMetadata> multiDiscFolders, IDirectoryService directoryService) - where T : Video, new() - { - var videoTypes = new List<VideoType>(); - - var folderPaths = multiDiscFolders.Select(i => i.FullName).Where(i => - { - var subFileEntries = directoryService.GetFileSystemEntries(i) - .ToList(); - - var subfolders = subFileEntries - .Where(e => e.IsDirectory) - .Select(d => d.Name) - .ToList(); - - if (subfolders.Any(IsDvdDirectory)) - { - videoTypes.Add(VideoType.Dvd); - return true; - } - if (subfolders.Any(IsBluRayDirectory)) - { - videoTypes.Add(VideoType.BluRay); - return true; - } - - var subFiles = subFileEntries - .Where(e => !e.IsDirectory) - .Select(d => d.Name); - - if (subFiles.Any(IsDvdFile)) - { - videoTypes.Add(VideoType.Dvd); - return true; - } - - return false; - - }).OrderBy(i => i).ToList(); - - // If different video types were found, don't allow this - if (videoTypes.Distinct().Count() > 1) - { - return null; - } - - if (folderPaths.Count == 0) - { - return null; - } - - var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions(); - var resolver = new StackResolver(namingOptions, new PatternsLogger()); - - var result = resolver.ResolveDirectories(folderPaths); - - if (result.Stacks.Count != 1) - { - return null; - } - - var returnVideo = new T - { - Path = folderPaths[0], - - AdditionalParts = folderPaths.Skip(1).ToList(), - - VideoType = videoTypes[0], - - Name = result.Stacks[0].Name - }; - - SetIsoType(returnVideo); - - return returnVideo; - } - - private bool IsInvalid(Folder parent, string collectionType) - { - if (parent != null) - { - if (parent.IsRoot) - { - return true; - } - } - - var validCollectionTypes = new[] - { - CollectionType.Movies, - CollectionType.HomeVideos, - CollectionType.MusicVideos, - CollectionType.Movies, - CollectionType.Photos - }; - - if (string.IsNullOrWhiteSpace(collectionType)) - { - return false; - } - - return !validCollectionTypes.Contains(collectionType, StringComparer.OrdinalIgnoreCase); - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs deleted file mode 100644 index 957fafb92..000000000 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs +++ /dev/null @@ -1,56 +0,0 @@ -using MediaBrowser.Controller.Drawing; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Resolvers; -using MediaBrowser.Model.Entities; -using System; -using System.IO; -using System.Linq; - -namespace MediaBrowser.Server.Implementations.Library.Resolvers -{ - public class PhotoAlbumResolver : FolderResolver<PhotoAlbum> - { - private readonly IImageProcessor _imageProcessor; - public PhotoAlbumResolver(IImageProcessor imageProcessor) - { - _imageProcessor = imageProcessor; - } - - /// <summary> - /// Resolves the specified args. - /// </summary> - /// <param name="args">The args.</param> - /// <returns>Trailer.</returns> - protected override PhotoAlbum Resolve(ItemResolveArgs args) - { - // Must be an image file within a photo collection - if (args.IsDirectory && string.Equals(args.GetCollectionType(), CollectionType.Photos, StringComparison.OrdinalIgnoreCase)) - { - if (HasPhotos(args)) - { - return new PhotoAlbum - { - Path = args.Path - }; - } - } - - return null; - } - - private bool HasPhotos(ItemResolveArgs args) - { - return args.FileSystemChildren.Any(i => (!i.IsDirectory) && PhotoResolver.IsImageFile(i.FullName, _imageProcessor)); - } - - public override ResolverPriority Priority - { - get - { - // Behind special folder resolver - return ResolverPriority.Second; - } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs deleted file mode 100644 index 549ad522e..000000000 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs +++ /dev/null @@ -1,103 +0,0 @@ -using MediaBrowser.Controller.Drawing; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Entities; -using System; -using System.IO; -using System.Linq; -using MediaBrowser.Common.IO; -using MediaBrowser.Model.IO; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.IO; -using MediaBrowser.Model.Configuration; - -namespace MediaBrowser.Server.Implementations.Library.Resolvers -{ - public class PhotoResolver : ItemResolver<Photo> - { - private readonly IImageProcessor _imageProcessor; - private readonly ILibraryManager _libraryManager; - - public PhotoResolver(IImageProcessor imageProcessor, ILibraryManager libraryManager) - { - _imageProcessor = imageProcessor; - _libraryManager = libraryManager; - } - - /// <summary> - /// Resolves the specified args. - /// </summary> - /// <param name="args">The args.</param> - /// <returns>Trailer.</returns> - protected override Photo Resolve(ItemResolveArgs args) - { - if (!args.IsDirectory) - { - // Must be an image file within a photo collection - var collectionType = args.GetCollectionType(); - - - if (string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase) || - (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) && args.GetLibraryOptions().EnablePhotos)) - { - if (IsImageFile(args.Path, _imageProcessor)) - { - var filename = Path.GetFileNameWithoutExtension(args.Path); - - // Make sure the image doesn't belong to a video file - if (args.DirectoryService.GetFiles(Path.GetDirectoryName(args.Path)).Any(i => IsOwnedByMedia(args.GetLibraryOptions(), i, filename))) - { - return null; - } - - return new Photo - { - Path = args.Path - }; - } - } - } - - return null; - } - - private bool IsOwnedByMedia(LibraryOptions libraryOptions, FileSystemMetadata file, string imageFilename) - { - if (_libraryManager.IsVideoFile(file.FullName, libraryOptions) && imageFilename.StartsWith(Path.GetFileNameWithoutExtension(file.Name), StringComparison.OrdinalIgnoreCase)) - { - return true; - } - - return false; - } - - private static readonly string[] IgnoreFiles = - { - "folder", - "thumb", - "landscape", - "fanart", - "backdrop", - "poster", - "cover" - }; - - internal static bool IsImageFile(string path, IImageProcessor imageProcessor) - { - var filename = Path.GetFileNameWithoutExtension(path) ?? string.Empty; - - if (IgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase)) - { - return false; - } - - if (IgnoreFiles.Any(i => filename.IndexOf(i, StringComparison.OrdinalIgnoreCase) != -1)) - { - return false; - } - - return imageProcessor.SupportedInputFormats.Contains((Path.GetExtension(path) ?? string.Empty).TrimStart('.'), StringComparer.OrdinalIgnoreCase); - } - - } -} diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/PlaylistResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/PlaylistResolver.cs deleted file mode 100644 index a95739f22..000000000 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/PlaylistResolver.cs +++ /dev/null @@ -1,42 +0,0 @@ -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Playlists; -using System; -using System.IO; - -namespace MediaBrowser.Server.Implementations.Library.Resolvers -{ - public class PlaylistResolver : FolderResolver<Playlist> - { - /// <summary> - /// Resolves the specified args. - /// </summary> - /// <param name="args">The args.</param> - /// <returns>BoxSet.</returns> - protected override Playlist Resolve(ItemResolveArgs args) - { - // It's a boxset if all of the following conditions are met: - // Is a Directory - // Contains [playlist] in the path - if (args.IsDirectory) - { - var filename = Path.GetFileName(args.Path); - - if (string.IsNullOrEmpty(filename)) - { - return null; - } - - if (filename.IndexOf("[playlist]", StringComparison.OrdinalIgnoreCase) != -1) - { - return new Playlist - { - Path = args.Path, - Name = ResolverHelper.StripBrackets(Path.GetFileName(args.Path)) - }; - } - } - - return null; - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs deleted file mode 100644 index 7a6198a00..000000000 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs +++ /dev/null @@ -1,85 +0,0 @@ -using MediaBrowser.Controller; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Resolvers; -using System; -using System.IO; -using System.Linq; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.IO; -using MediaBrowser.Model.IO; - -namespace MediaBrowser.Server.Implementations.Library.Resolvers -{ - class SpecialFolderResolver : FolderResolver<Folder> - { - private readonly IFileSystem _fileSystem; - private readonly IServerApplicationPaths _appPaths; - - public SpecialFolderResolver(IFileSystem fileSystem, IServerApplicationPaths appPaths) - { - _fileSystem = fileSystem; - _appPaths = appPaths; - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override ResolverPriority Priority - { - get { return ResolverPriority.First; } - } - - /// <summary> - /// Resolves the specified args. - /// </summary> - /// <param name="args">The args.</param> - /// <returns>Folder.</returns> - protected override Folder Resolve(ItemResolveArgs args) - { - if (args.IsDirectory) - { - if (args.IsPhysicalRoot) - { - return new AggregateFolder(); - } - if (string.Equals(args.Path, _appPaths.DefaultUserViewsPath, StringComparison.OrdinalIgnoreCase)) - { - return new UserRootFolder(); //if we got here and still a root - must be user root - } - if (args.IsVf) - { - return new CollectionFolder - { - CollectionType = GetCollectionType(args), - PhysicalLocationsList = args.PhysicalLocations.ToList() - }; - } - } - - return null; - } - - private string GetCollectionType(ItemResolveArgs args) - { - return args.FileSystemChildren - .Where(i => - { - - try - { - return !i.IsDirectory && - string.Equals(".collection", i.Extension, StringComparison.OrdinalIgnoreCase); - } - catch (IOException) - { - return false; - } - - }) - .Select(i => _fileSystem.GetFileNameWithoutExtension(i)) - .FirstOrDefault(); - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs deleted file mode 100644 index 6edc4a009..000000000 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; -using System.Linq; -using MediaBrowser.Model.Entities; - -namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV -{ - /// <summary> - /// Class EpisodeResolver - /// </summary> - public class EpisodeResolver : BaseVideoResolver<Episode> - { - public EpisodeResolver(ILibraryManager libraryManager) : base(libraryManager) - { - } - - /// <summary> - /// Resolves the specified args. - /// </summary> - /// <param name="args">The args.</param> - /// <returns>Episode.</returns> - protected override Episode Resolve(ItemResolveArgs args) - { - var parent = args.Parent; - - if (parent == null) - { - return null; - } - - var season = parent as Season; - // Just in case the user decided to nest episodes. - // Not officially supported but in some cases we can handle it. - if (season == null) - { - season = parent.GetParents().OfType<Season>().FirstOrDefault(); - } - - // If the parent is a Season or Series, then this is an Episode if the VideoResolver returns something - // Also handle flat tv folders - if (season != null || - string.Equals(args.GetCollectionType(), CollectionType.TvShows, StringComparison.OrdinalIgnoreCase) || - args.HasParent<Series>()) - { - var episode = ResolveVideo<Episode>(args, false); - - if (episode != null) - { - var series = parent as Series; - if (series == null) - { - series = parent.GetParents().OfType<Series>().FirstOrDefault(); - } - - if (series != null) - { - episode.SeriesId = series.Id; - episode.SeriesName = series.Name; - episode.SeriesSortName = series.SortName; - } - if (season != null) - { - episode.SeasonId = season.Id; - episode.SeasonName = season.Name; - } - } - - return episode; - } - - return null; - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs deleted file mode 100644 index fc4929748..000000000 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs +++ /dev/null @@ -1,62 +0,0 @@ -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; -using MediaBrowser.Naming.Common; -using MediaBrowser.Naming.TV; - -namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV -{ - /// <summary> - /// Class SeasonResolver - /// </summary> - public class SeasonResolver : FolderResolver<Season> - { - /// <summary> - /// The _config - /// </summary> - private readonly IServerConfigurationManager _config; - - private readonly ILibraryManager _libraryManager; - - /// <summary> - /// Initializes a new instance of the <see cref="SeasonResolver"/> class. - /// </summary> - /// <param name="config">The config.</param> - public SeasonResolver(IServerConfigurationManager config, ILibraryManager libraryManager) - { - _config = config; - _libraryManager = libraryManager; - } - - /// <summary> - /// Resolves the specified args. - /// </summary> - /// <param name="args">The args.</param> - /// <returns>Season.</returns> - protected override Season Resolve(ItemResolveArgs args) - { - if (args.Parent is Series && args.IsDirectory) - { - var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions(); - var series = ((Series)args.Parent); - - var season = new Season - { - IndexNumber = new SeasonPathParser(namingOptions, new RegexProvider()).Parse(args.Path, true, true).SeasonNumber, - SeriesId = series.Id, - SeriesSortName = series.SortName, - SeriesName = series.Name - }; - - if (season.IndexNumber.HasValue && season.IndexNumber.Value == 0) - { - season.Name = _config.Configuration.SeasonZeroDisplayName; - } - - return season; - } - - return null; - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs deleted file mode 100644 index 83566e2c1..000000000 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs +++ /dev/null @@ -1,251 +0,0 @@ -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Controller.Resolvers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Naming.Common; -using MediaBrowser.Naming.TV; -using MediaBrowser.Server.Implementations.Logging; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using MediaBrowser.Common.IO; -using MediaBrowser.Model.IO; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.IO; -using MediaBrowser.Model.Configuration; - -namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV -{ - /// <summary> - /// Class SeriesResolver - /// </summary> - public class SeriesResolver : FolderResolver<Series> - { - private readonly IFileSystem _fileSystem; - private readonly ILogger _logger; - private readonly ILibraryManager _libraryManager; - - public SeriesResolver(IFileSystem fileSystem, ILogger logger, ILibraryManager libraryManager) - { - _fileSystem = fileSystem; - _logger = logger; - _libraryManager = libraryManager; - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override ResolverPriority Priority - { - get - { - return ResolverPriority.Second; - } - } - - /// <summary> - /// Resolves the specified args. - /// </summary> - /// <param name="args">The args.</param> - /// <returns>Series.</returns> - protected override Series Resolve(ItemResolveArgs args) - { - if (args.IsDirectory) - { - if (args.HasParent<Series>() || args.HasParent<Season>()) - { - return null; - } - - var collectionType = args.GetCollectionType(); - if (string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase)) - { - //if (args.ContainsFileSystemEntryByName("tvshow.nfo")) - //{ - // return new Series - // { - // Path = args.Path, - // Name = Path.GetFileName(args.Path) - // }; - //} - - var configuredContentType = _libraryManager.GetConfiguredContentType(args.Path); - if (!string.Equals(configuredContentType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase)) - { - return new Series - { - Path = args.Path, - Name = Path.GetFileName(args.Path) - }; - } - } - else if (string.IsNullOrWhiteSpace(collectionType)) - { - if (args.ContainsFileSystemEntryByName("tvshow.nfo")) - { - if (args.Parent.IsRoot) - { - // For now, return null, but if we want to allow this in the future then add some additional checks to guard against a misplaced tvshow.nfo - return null; - } - - return new Series - { - Path = args.Path, - Name = Path.GetFileName(args.Path) - }; - } - - if (args.Parent.IsRoot) - { - return null; - } - - if (IsSeriesFolder(args.Path, args.FileSystemChildren, args.DirectoryService, _fileSystem, _logger, _libraryManager, args.GetLibraryOptions(), false)) - { - return new Series - { - Path = args.Path, - Name = Path.GetFileName(args.Path) - }; - } - } - } - - return null; - } - - public static bool IsSeriesFolder(string path, - IEnumerable<FileSystemMetadata> fileSystemChildren, - IDirectoryService directoryService, - IFileSystem fileSystem, - ILogger logger, - ILibraryManager libraryManager, - LibraryOptions libraryOptions, - bool isTvContentType) - { - foreach (var child in fileSystemChildren) - { - //if ((attributes & FileAttributes.Hidden) == FileAttributes.Hidden) - //{ - // //logger.Debug("Igoring series file or folder marked hidden: {0}", child.FullName); - // continue; - //} - - // Can't enforce this because files saved by Bitcasa are always marked System - //if ((attributes & FileAttributes.System) == FileAttributes.System) - //{ - // logger.Debug("Igoring series subfolder marked system: {0}", child.FullName); - // continue; - //} - - if (child.IsDirectory) - { - if (IsSeasonFolder(child.FullName, isTvContentType, libraryManager)) - { - //logger.Debug("{0} is a series because of season folder {1}.", path, child.FullName); - return true; - } - } - else - { - string fullName = child.FullName; - if (libraryManager.IsVideoFile(fullName, libraryOptions)) - { - if (isTvContentType) - { - return true; - } - - var namingOptions = ((LibraryManager)libraryManager).GetNamingOptions(); - - // In mixed folders we need to be conservative and avoid expressions that may result in false positives (e.g. movies with numbers in the title) - if (!isTvContentType) - { - namingOptions.EpisodeExpressions = namingOptions.EpisodeExpressions - .Where(i => i.IsNamed && !i.IsOptimistic) - .ToList(); - } - - var episodeResolver = new Naming.TV.EpisodeResolver(namingOptions, new PatternsLogger()); - var episodeInfo = episodeResolver.Resolve(fullName, false, false); - if (episodeInfo != null && episodeInfo.EpisodeNumber.HasValue) - { - return true; - } - } - } - } - - logger.Debug("{0} is not a series folder.", path); - return false; - } - - /// <summary> - /// Determines whether [is place holder] [the specified path]. - /// </summary> - /// <param name="path">The path.</param> - /// <returns><c>true</c> if [is place holder] [the specified path]; otherwise, <c>false</c>.</returns> - /// <exception cref="System.ArgumentNullException">path</exception> - private static bool IsVideoPlaceHolder(string path) - { - if (string.IsNullOrEmpty(path)) - { - throw new ArgumentNullException("path"); - } - - var extension = Path.GetExtension(path); - - return string.Equals(extension, ".disc", StringComparison.OrdinalIgnoreCase); - } - - /// <summary> - /// Determines whether [is season folder] [the specified path]. - /// </summary> - /// <param name="path">The path.</param> - /// <param name="isTvContentType">if set to <c>true</c> [is tv content type].</param> - /// <param name="libraryManager">The library manager.</param> - /// <returns><c>true</c> if [is season folder] [the specified path]; otherwise, <c>false</c>.</returns> - private static bool IsSeasonFolder(string path, bool isTvContentType, ILibraryManager libraryManager) - { - var namingOptions = ((LibraryManager)libraryManager).GetNamingOptions(); - - var seasonNumber = new SeasonPathParser(namingOptions, new RegexProvider()).Parse(path, isTvContentType, isTvContentType).SeasonNumber; - - return seasonNumber.HasValue; - } - - /// <summary> - /// Sets the initial item values. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="args">The args.</param> - protected override void SetInitialItemValues(Series item, ItemResolveArgs args) - { - base.SetInitialItemValues(item, args); - - SetProviderIdFromPath(item, args.Path); - } - - /// <summary> - /// Sets the provider id from path. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="path">The path.</param> - private void SetProviderIdFromPath(Series item, string path) - { - var justName = Path.GetFileName(path); - - var id = justName.GetAttributeValue("tvdbid"); - - if (!string.IsNullOrEmpty(id)) - { - item.SetProviderId(MetadataProviders.Tvdb, id); - } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/VideoResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/VideoResolver.cs deleted file mode 100644 index c7f21cef1..000000000 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/VideoResolver.cs +++ /dev/null @@ -1,45 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Resolvers; - -namespace MediaBrowser.Server.Implementations.Library.Resolvers -{ - /// <summary> - /// Resolves a Path into a Video - /// </summary> - public class VideoResolver : BaseVideoResolver<Video> - { - public VideoResolver(ILibraryManager libraryManager) - : base(libraryManager) - { - } - - protected override Video Resolve(ItemResolveArgs args) - { - if (args.Parent != null) - { - // The movie resolver will handle this - return null; - } - - return base.Resolve(args); - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override ResolverPriority Priority - { - get { return ResolverPriority.Last; } - } - } - - public class GenericVideoResolver<T> : BaseVideoResolver<T> - where T : Video, new () - { - public GenericVideoResolver(ILibraryManager libraryManager) : base(libraryManager) - { - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/SearchEngine.cs b/MediaBrowser.Server.Implementations/Library/SearchEngine.cs deleted file mode 100644 index c266fb191..000000000 --- a/MediaBrowser.Server.Implementations/Library/SearchEngine.cs +++ /dev/null @@ -1,280 +0,0 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Querying; -using MediaBrowser.Model.Search; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using MediaBrowser.Controller.Extensions; -using MediaBrowser.Model.Extensions; - -namespace MediaBrowser.Server.Implementations.Library -{ - /// <summary> - /// Class LuceneSearchEngine - /// http://www.codeproject.com/Articles/320219/Lucene-Net-ultra-fast-search-for-MVC-or-WebForms - /// </summary> - public class SearchEngine : ISearchEngine - { - private readonly ILibraryManager _libraryManager; - private readonly IUserManager _userManager; - private readonly ILogger _logger; - - public SearchEngine(ILogManager logManager, ILibraryManager libraryManager, IUserManager userManager) - { - _libraryManager = libraryManager; - _userManager = userManager; - - _logger = logManager.GetLogger("Lucene"); - } - - public async Task<QueryResult<SearchHintInfo>> GetSearchHints(SearchQuery query) - { - User user = null; - - if (string.IsNullOrWhiteSpace(query.UserId)) - { - } - else - { - user = _userManager.GetUserById(query.UserId); - } - - var results = await GetSearchHints(query, user).ConfigureAwait(false); - - var searchResultArray = results.ToArray(); - results = searchResultArray; - - var count = searchResultArray.Length; - - if (query.StartIndex.HasValue) - { - results = results.Skip(query.StartIndex.Value); - } - - if (query.Limit.HasValue) - { - results = results.Take(query.Limit.Value); - } - - return new QueryResult<SearchHintInfo> - { - TotalRecordCount = count, - - Items = results.ToArray() - }; - } - - private void AddIfMissing(List<string> list, string value) - { - if (!list.Contains(value, StringComparer.OrdinalIgnoreCase)) - { - list.Add(value); - } - } - - /// <summary> - /// Gets the search hints. - /// </summary> - /// <param name="query">The query.</param> - /// <param name="user">The user.</param> - /// <returns>IEnumerable{SearchHintResult}.</returns> - /// <exception cref="System.ArgumentNullException">searchTerm</exception> - private Task<IEnumerable<SearchHintInfo>> GetSearchHints(SearchQuery query, User user) - { - var searchTerm = query.SearchTerm; - - if (searchTerm != null) - { - searchTerm = searchTerm.Trim().RemoveDiacritics(); - } - - if (string.IsNullOrWhiteSpace(searchTerm)) - { - throw new ArgumentNullException("searchTerm"); - } - - var terms = GetWords(searchTerm); - - var hints = new List<Tuple<BaseItem, string, int>>(); - - var excludeItemTypes = new List<string>(); - var includeItemTypes = (query.IncludeItemTypes ?? new string[] { }).ToList(); - - excludeItemTypes.Add(typeof(Year).Name); - excludeItemTypes.Add(typeof(Folder).Name); - - if (query.IncludeGenres && (includeItemTypes.Count == 0 || includeItemTypes.Contains("Genre", StringComparer.OrdinalIgnoreCase))) - { - if (!query.IncludeMedia) - { - AddIfMissing(includeItemTypes, typeof(Genre).Name); - AddIfMissing(includeItemTypes, typeof(GameGenre).Name); - AddIfMissing(includeItemTypes, typeof(MusicGenre).Name); - } - } - else - { - AddIfMissing(excludeItemTypes, typeof(Genre).Name); - AddIfMissing(excludeItemTypes, typeof(GameGenre).Name); - AddIfMissing(excludeItemTypes, typeof(MusicGenre).Name); - } - - if (query.IncludePeople && (includeItemTypes.Count == 0 || includeItemTypes.Contains("People", StringComparer.OrdinalIgnoreCase) || includeItemTypes.Contains("Person", StringComparer.OrdinalIgnoreCase))) - { - if (!query.IncludeMedia) - { - AddIfMissing(includeItemTypes, typeof(Person).Name); - } - } - else - { - AddIfMissing(excludeItemTypes, typeof(Person).Name); - } - - if (query.IncludeStudios && (includeItemTypes.Count == 0 || includeItemTypes.Contains("Studio", StringComparer.OrdinalIgnoreCase))) - { - if (!query.IncludeMedia) - { - AddIfMissing(includeItemTypes, typeof(Studio).Name); - } - } - else - { - AddIfMissing(excludeItemTypes, typeof(Studio).Name); - } - - if (query.IncludeArtists && (includeItemTypes.Count == 0 || includeItemTypes.Contains("MusicArtist", StringComparer.OrdinalIgnoreCase))) - { - if (!query.IncludeMedia) - { - AddIfMissing(includeItemTypes, typeof(MusicArtist).Name); - } - } - else - { - AddIfMissing(excludeItemTypes, typeof(MusicArtist).Name); - } - - AddIfMissing(excludeItemTypes, typeof(CollectionFolder).Name); - - var mediaItems = _libraryManager.GetItemList(new InternalItemsQuery(user) - { - NameContains = searchTerm, - ExcludeItemTypes = excludeItemTypes.ToArray(), - IncludeItemTypes = includeItemTypes.ToArray(), - Limit = query.Limit, - IncludeItemsByName = true, - IsVirtualItem = false - }); - - // Add search hints based on item name - hints.AddRange(mediaItems.Select(item => - { - var index = GetIndex(item.Name, searchTerm, terms); - - return new Tuple<BaseItem, string, int>(item, index.Item1, index.Item2); - })); - - var returnValue = hints.Where(i => i.Item3 >= 0).OrderBy(i => i.Item3).Select(i => new SearchHintInfo - { - Item = i.Item1, - MatchedTerm = i.Item2 - }); - - return Task.FromResult(returnValue); - } - - /// <summary> - /// Gets the index. - /// </summary> - /// <param name="input">The input.</param> - /// <param name="searchInput">The search input.</param> - /// <param name="searchWords">The search input.</param> - /// <returns>System.Int32.</returns> - private Tuple<string, int> GetIndex(string input, string searchInput, List<string> searchWords) - { - if (string.IsNullOrWhiteSpace(input)) - { - throw new ArgumentNullException("input"); - } - - input = input.RemoveDiacritics(); - - if (string.Equals(input, searchInput, StringComparison.OrdinalIgnoreCase)) - { - return new Tuple<string, int>(searchInput, 0); - } - - var index = input.IndexOf(searchInput, StringComparison.OrdinalIgnoreCase); - - if (index == 0) - { - return new Tuple<string, int>(searchInput, 1); - } - if (index > 0) - { - return new Tuple<string, int>(searchInput, 2); - } - - var items = GetWords(input); - - for (var i = 0; i < searchWords.Count; i++) - { - var searchTerm = searchWords[i]; - - for (var j = 0; j < items.Count; j++) - { - var item = items[j]; - - if (string.Equals(item, searchTerm, StringComparison.OrdinalIgnoreCase)) - { - return new Tuple<string, int>(searchTerm, 3 + (i + 1) * (j + 1)); - } - - index = item.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase); - - if (index == 0) - { - return new Tuple<string, int>(searchTerm, 4 + (i + 1) * (j + 1)); - } - if (index > 0) - { - return new Tuple<string, int>(searchTerm, 5 + (i + 1) * (j + 1)); - } - } - } - return new Tuple<string, int>(null, -1); - } - - /// <summary> - /// Gets the words. - /// </summary> - /// <param name="term">The term.</param> - /// <returns>System.String[][].</returns> - private List<string> GetWords(string term) - { - var stoplist = GetStopList().ToList(); - - return term.Split() - .Where(i => !string.IsNullOrWhiteSpace(i) && !stoplist.Contains(i, StringComparer.OrdinalIgnoreCase)) - .ToList(); - } - - private IEnumerable<string> GetStopList() - { - return new[] - { - "the", - "a", - "of", - "an" - }; - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs deleted file mode 100644 index 6a4e26ff9..000000000 --- a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs +++ /dev/null @@ -1,292 +0,0 @@ -using MediaBrowser.Controller.Channels; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Model.Channels; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Library; -using MediaBrowser.Model.Querying; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Model.Globalization; - -namespace MediaBrowser.Server.Implementations.Library -{ - public class UserViewManager : IUserViewManager - { - private readonly ILibraryManager _libraryManager; - private readonly ILocalizationManager _localizationManager; - private readonly IUserManager _userManager; - - private readonly IChannelManager _channelManager; - private readonly ILiveTvManager _liveTvManager; - private readonly IServerConfigurationManager _config; - - public UserViewManager(ILibraryManager libraryManager, ILocalizationManager localizationManager, IUserManager userManager, IChannelManager channelManager, ILiveTvManager liveTvManager, IServerConfigurationManager config) - { - _libraryManager = libraryManager; - _localizationManager = localizationManager; - _userManager = userManager; - _channelManager = channelManager; - _liveTvManager = liveTvManager; - _config = config; - } - - public async Task<IEnumerable<Folder>> GetUserViews(UserViewQuery query, CancellationToken cancellationToken) - { - var user = _userManager.GetUserById(query.UserId); - - var folders = user.RootFolder - .GetChildren(user, true) - .OfType<Folder>() - .ToList(); - - if (!query.IncludeHidden) - { - folders = folders.Where(i => - { - var hidden = i as IHiddenFromDisplay; - return hidden == null || !hidden.IsHiddenFromUser(user); - }).ToList(); - } - - var plainFolderIds = user.Configuration.PlainFolderViews.Select(i => new Guid(i)).ToList(); - - var groupedFolders = new List<ICollectionFolder>(); - - var list = new List<Folder>(); - - foreach (var folder in folders) - { - var collectionFolder = folder as ICollectionFolder; - var folderViewType = collectionFolder == null ? null : collectionFolder.CollectionType; - - if (UserView.IsUserSpecific(folder)) - { - list.Add(await _libraryManager.GetNamedView(user, folder.Name, folder.Id.ToString("N"), folderViewType, null, cancellationToken).ConfigureAwait(false)); - continue; - } - - if (plainFolderIds.Contains(folder.Id) && UserView.IsEligibleForEnhancedView(folderViewType)) - { - list.Add(folder); - continue; - } - - if (collectionFolder != null && UserView.IsEligibleForGrouping(folder) && user.IsFolderGrouped(folder.Id)) - { - groupedFolders.Add(collectionFolder); - continue; - } - - if (query.PresetViews.Contains(folderViewType ?? string.Empty, StringComparer.OrdinalIgnoreCase)) - { - list.Add(await GetUserView(folder, folderViewType, string.Empty, cancellationToken).ConfigureAwait(false)); - } - else - { - list.Add(folder); - } - } - - foreach (var viewType in new[] { CollectionType.Movies, CollectionType.TvShows }) - { - var parents = groupedFolders.Where(i => string.Equals(i.CollectionType, viewType, StringComparison.OrdinalIgnoreCase) || string.IsNullOrWhiteSpace(i.CollectionType)) - .ToList(); - - if (parents.Count > 0) - { - list.Add(await GetUserView(parents, viewType, string.Empty, user, query.PresetViews, cancellationToken).ConfigureAwait(false)); - } - } - - if (_config.Configuration.EnableFolderView) - { - var name = _localizationManager.GetLocalizedString("ViewType" + CollectionType.Folders); - list.Add(await _libraryManager.GetNamedView(name, CollectionType.Folders, string.Empty, cancellationToken).ConfigureAwait(false)); - } - - if (query.IncludeExternalContent) - { - var channelResult = await _channelManager.GetChannelsInternal(new ChannelQuery - { - UserId = query.UserId - - }, cancellationToken).ConfigureAwait(false); - - var channels = channelResult.Items; - - if (_config.Configuration.EnableChannelView && channels.Length > 0) - { - list.Add(await _channelManager.GetInternalChannelFolder(cancellationToken).ConfigureAwait(false)); - } - else - { - list.AddRange(channels); - } - - if (_liveTvManager.GetEnabledUsers().Select(i => i.Id.ToString("N")).Contains(query.UserId)) - { - list.Add(await _liveTvManager.GetInternalLiveTvFolder(CancellationToken.None).ConfigureAwait(false)); - } - } - - var sorted = _libraryManager.Sort(list, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).ToList(); - - var orders = user.Configuration.OrderedViews.ToList(); - - return list - .OrderBy(i => - { - var index = orders.IndexOf(i.Id.ToString("N")); - - if (index == -1) - { - var view = i as UserView; - if (view != null) - { - if (view.DisplayParentId != Guid.Empty) - { - index = orders.IndexOf(view.DisplayParentId.ToString("N")); - } - } - } - - return index == -1 ? int.MaxValue : index; - }) - .ThenBy(sorted.IndexOf) - .ThenBy(i => i.SortName); - } - - public Task<UserView> GetUserSubView(string name, string parentId, string type, string sortName, CancellationToken cancellationToken) - { - var uniqueId = parentId + "subview" + type; - - return _libraryManager.GetNamedView(name, parentId, type, sortName, uniqueId, cancellationToken); - } - - public Task<UserView> GetUserSubView(string parentId, string type, string sortName, CancellationToken cancellationToken) - { - var name = _localizationManager.GetLocalizedString("ViewType" + type); - - return GetUserSubView(name, parentId, type, sortName, cancellationToken); - } - - private async Task<Folder> GetUserView(List<ICollectionFolder> parents, string viewType, string sortName, User user, string[] presetViews, CancellationToken cancellationToken) - { - if (parents.Count == 1 && parents.All(i => string.Equals(i.CollectionType, viewType, StringComparison.OrdinalIgnoreCase))) - { - if (!presetViews.Contains(viewType, StringComparer.OrdinalIgnoreCase)) - { - return (Folder)parents[0]; - } - - return await GetUserView((Folder)parents[0], viewType, string.Empty, cancellationToken).ConfigureAwait(false); - } - - var name = _localizationManager.GetLocalizedString("ViewType" + viewType); - return await _libraryManager.GetNamedView(user, name, viewType, sortName, cancellationToken).ConfigureAwait(false); - } - - public Task<UserView> GetUserView(Folder parent, string viewType, string sortName, CancellationToken cancellationToken) - { - return _libraryManager.GetShadowView(parent, viewType, sortName, cancellationToken); - } - - public List<Tuple<BaseItem, List<BaseItem>>> GetLatestItems(LatestItemsQuery request) - { - var user = _userManager.GetUserById(request.UserId); - - var libraryItems = GetItemsForLatestItems(user, request); - - var list = new List<Tuple<BaseItem, List<BaseItem>>>(); - - foreach (var item in libraryItems) - { - // Only grab the index container for media - var container = item.IsFolder || !request.GroupItems ? null : item.LatestItemsIndexContainer; - - if (container == null) - { - list.Add(new Tuple<BaseItem, List<BaseItem>>(null, new List<BaseItem> { item })); - } - else - { - var current = list.FirstOrDefault(i => i.Item1 != null && i.Item1.Id == container.Id); - - if (current != null) - { - current.Item2.Add(item); - } - else - { - list.Add(new Tuple<BaseItem, List<BaseItem>>(container, new List<BaseItem> { item })); - } - } - - if (list.Count >= request.Limit) - { - break; - } - } - - return list; - } - - private IEnumerable<BaseItem> GetItemsForLatestItems(User user, LatestItemsQuery request) - { - var parentId = request.ParentId; - - var includeItemTypes = request.IncludeItemTypes; - var limit = request.Limit ?? 10; - - var parentIds = string.IsNullOrEmpty(parentId) - ? new string[] { } - : new[] { parentId }; - - if (parentIds.Length == 0) - { - parentIds = user.RootFolder.GetChildren(user, true) - .OfType<Folder>() - .Select(i => i.Id.ToString("N")) - .Where(i => !user.Configuration.LatestItemsExcludes.Contains(i)) - .ToArray(); - } - - if (parentIds.Length == 0) - { - return new List<BaseItem>(); - } - - var excludeItemTypes = includeItemTypes.Length == 0 ? new[] - { - typeof(Person).Name, - typeof(Studio).Name, - typeof(Year).Name, - typeof(GameGenre).Name, - typeof(MusicGenre).Name, - typeof(Genre).Name - - } : new string[] { }; - - return _libraryManager.GetItemList(new InternalItemsQuery(user) - { - IncludeItemTypes = includeItemTypes, - SortOrder = SortOrder.Descending, - SortBy = new[] { ItemSortBy.DateCreated }, - IsFolder = includeItemTypes.Length == 0 ? false : (bool?)null, - ExcludeItemTypes = excludeItemTypes, - ExcludeLocationTypes = new[] { LocationType.Virtual }, - Limit = limit * 5, - SourceTypes = parentIds.Length == 0 ? new[] { SourceType.Library } : new SourceType[] { }, - IsPlayed = request.IsPlayed - - }, parentIds); - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs b/MediaBrowser.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs deleted file mode 100644 index 91b035a35..000000000 --- a/MediaBrowser.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs +++ /dev/null @@ -1,44 +0,0 @@ -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Logging; -using System; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Controller.Persistence; - -namespace MediaBrowser.Server.Implementations.Library.Validators -{ - /// <summary> - /// Class ArtistsPostScanTask - /// </summary> - public class ArtistsPostScanTask : ILibraryPostScanTask - { - /// <summary> - /// The _library manager - /// </summary> - private readonly ILibraryManager _libraryManager; - private readonly ILogger _logger; - private readonly IItemRepository _itemRepo; - - /// <summary> - /// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class. - /// </summary> - /// <param name="libraryManager">The library manager.</param> - public ArtistsPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo) - { - _libraryManager = libraryManager; - _logger = logger; - _itemRepo = itemRepo; - } - - /// <summary> - /// Runs the specified progress. - /// </summary> - /// <param name="progress">The progress.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public Task Run(IProgress<double> progress, CancellationToken cancellationToken) - { - return new ArtistsValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken); - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/Validators/ArtistsValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/ArtistsValidator.cs deleted file mode 100644 index 3dcdbeae9..000000000 --- a/MediaBrowser.Server.Implementations/Library/Validators/ArtistsValidator.cs +++ /dev/null @@ -1,84 +0,0 @@ -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Logging; -using System; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Persistence; - -namespace MediaBrowser.Server.Implementations.Library.Validators -{ - /// <summary> - /// Class ArtistsValidator - /// </summary> - public class ArtistsValidator - { - /// <summary> - /// The _library manager - /// </summary> - private readonly ILibraryManager _libraryManager; - - /// <summary> - /// The _logger - /// </summary> - private readonly ILogger _logger; - private readonly IItemRepository _itemRepo; - - /// <summary> - /// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class. - /// </summary> - /// <param name="libraryManager">The library manager.</param> - /// <param name="logger">The logger.</param> - public ArtistsValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo) - { - _libraryManager = libraryManager; - _logger = logger; - _itemRepo = itemRepo; - } - - /// <summary> - /// Runs the specified progress. - /// </summary> - /// <param name="progress">The progress.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public async Task Run(IProgress<double> progress, CancellationToken cancellationToken) - { - var names = _itemRepo.GetAllArtistNames(); - - var numComplete = 0; - var count = names.Count; - - foreach (var name in names) - { - try - { - var item = _libraryManager.GetArtist(name); - - await item.RefreshMetadata(cancellationToken).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - // Don't clutter the log - break; - } - catch (Exception ex) - { - _logger.ErrorException("Error refreshing {0}", ex, name); - } - - numComplete++; - double percent = numComplete; - percent /= count; - percent *= 100; - - progress.Report(percent); - } - - progress.Report(100); - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/Validators/GameGenresPostScanTask.cs b/MediaBrowser.Server.Implementations/Library/Validators/GameGenresPostScanTask.cs deleted file mode 100644 index f3891180e..000000000 --- a/MediaBrowser.Server.Implementations/Library/Validators/GameGenresPostScanTask.cs +++ /dev/null @@ -1,45 +0,0 @@ -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Logging; -using System; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Controller.Persistence; - -namespace MediaBrowser.Server.Implementations.Library.Validators -{ - /// <summary> - /// Class GameGenresPostScanTask - /// </summary> - public class GameGenresPostScanTask : ILibraryPostScanTask - { - /// <summary> - /// The _library manager - /// </summary> - private readonly ILibraryManager _libraryManager; - private readonly ILogger _logger; - private readonly IItemRepository _itemRepo; - - /// <summary> - /// Initializes a new instance of the <see cref="GameGenresPostScanTask" /> class. - /// </summary> - /// <param name="libraryManager">The library manager.</param> - /// <param name="logger">The logger.</param> - public GameGenresPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo) - { - _libraryManager = libraryManager; - _logger = logger; - _itemRepo = itemRepo; - } - - /// <summary> - /// Runs the specified progress. - /// </summary> - /// <param name="progress">The progress.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public Task Run(IProgress<double> progress, CancellationToken cancellationToken) - { - return new GameGenresValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken); - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/Validators/GameGenresValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/GameGenresValidator.cs deleted file mode 100644 index b06c0b3b9..000000000 --- a/MediaBrowser.Server.Implementations/Library/Validators/GameGenresValidator.cs +++ /dev/null @@ -1,74 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Logging; -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Controller.Persistence; - -namespace MediaBrowser.Server.Implementations.Library.Validators -{ - class GameGenresValidator - { - /// <summary> - /// The _library manager - /// </summary> - private readonly ILibraryManager _libraryManager; - - /// <summary> - /// The _logger - /// </summary> - private readonly ILogger _logger; - private readonly IItemRepository _itemRepo; - - public GameGenresValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo) - { - _libraryManager = libraryManager; - _logger = logger; - _itemRepo = itemRepo; - } - - /// <summary> - /// Runs the specified progress. - /// </summary> - /// <param name="progress">The progress.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public async Task Run(IProgress<double> progress, CancellationToken cancellationToken) - { - var names = _itemRepo.GetGameGenreNames(); - - var numComplete = 0; - var count = names.Count; - - foreach (var name in names) - { - try - { - var item = _libraryManager.GetGameGenre(name); - - await item.RefreshMetadata(cancellationToken).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - // Don't clutter the log - break; - } - catch (Exception ex) - { - _logger.ErrorException("Error refreshing {0}", ex, name); - } - - numComplete++; - double percent = numComplete; - percent /= count; - percent *= 100; - - progress.Report(percent); - } - - progress.Report(100); - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/Validators/GenresPostScanTask.cs b/MediaBrowser.Server.Implementations/Library/Validators/GenresPostScanTask.cs deleted file mode 100644 index ed2429769..000000000 --- a/MediaBrowser.Server.Implementations/Library/Validators/GenresPostScanTask.cs +++ /dev/null @@ -1,42 +0,0 @@ -using MediaBrowser.Controller.Library; -using System; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Controller.Persistence; -using MediaBrowser.Model.Logging; - -namespace MediaBrowser.Server.Implementations.Library.Validators -{ - public class GenresPostScanTask : ILibraryPostScanTask - { - /// <summary> - /// The _library manager - /// </summary> - private readonly ILibraryManager _libraryManager; - private readonly ILogger _logger; - private readonly IItemRepository _itemRepo; - - /// <summary> - /// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class. - /// </summary> - /// <param name="libraryManager">The library manager.</param> - /// <param name="logger">The logger.</param> - public GenresPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo) - { - _libraryManager = libraryManager; - _logger = logger; - _itemRepo = itemRepo; - } - - /// <summary> - /// Runs the specified progress. - /// </summary> - /// <param name="progress">The progress.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public Task Run(IProgress<double> progress, CancellationToken cancellationToken) - { - return new GenresValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken); - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/Validators/GenresValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/GenresValidator.cs deleted file mode 100644 index f35bb5136..000000000 --- a/MediaBrowser.Server.Implementations/Library/Validators/GenresValidator.cs +++ /dev/null @@ -1,75 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Logging; -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Controller.Persistence; - -namespace MediaBrowser.Server.Implementations.Library.Validators -{ - class GenresValidator - { - /// <summary> - /// The _library manager - /// </summary> - private readonly ILibraryManager _libraryManager; - private readonly IItemRepository _itemRepo; - - /// <summary> - /// The _logger - /// </summary> - private readonly ILogger _logger; - - public GenresValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo) - { - _libraryManager = libraryManager; - _logger = logger; - _itemRepo = itemRepo; - } - - /// <summary> - /// Runs the specified progress. - /// </summary> - /// <param name="progress">The progress.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public async Task Run(IProgress<double> progress, CancellationToken cancellationToken) - { - var names = _itemRepo.GetGenreNames(); - - var numComplete = 0; - var count = names.Count; - - foreach (var name in names) - { - try - { - var item = _libraryManager.GetGenre(name); - - await item.RefreshMetadata(cancellationToken).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - // Don't clutter the log - break; - } - catch (Exception ex) - { - _logger.ErrorException("Error refreshing {0}", ex, name); - } - - numComplete++; - double percent = numComplete; - percent /= count; - percent *= 100; - - progress.Report(percent); - } - - progress.Report(100); - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs b/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs deleted file mode 100644 index 777532ff8..000000000 --- a/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs +++ /dev/null @@ -1,45 +0,0 @@ -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Logging; -using System; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Controller.Persistence; - -namespace MediaBrowser.Server.Implementations.Library.Validators -{ - /// <summary> - /// Class MusicGenresPostScanTask - /// </summary> - public class MusicGenresPostScanTask : ILibraryPostScanTask - { - /// <summary> - /// The _library manager - /// </summary> - private readonly ILibraryManager _libraryManager; - private readonly ILogger _logger; - private readonly IItemRepository _itemRepo; - - /// <summary> - /// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class. - /// </summary> - /// <param name="libraryManager">The library manager.</param> - /// <param name="logger">The logger.</param> - public MusicGenresPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo) - { - _libraryManager = libraryManager; - _logger = logger; - _itemRepo = itemRepo; - } - - /// <summary> - /// Runs the specified progress. - /// </summary> - /// <param name="progress">The progress.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public Task Run(IProgress<double> progress, CancellationToken cancellationToken) - { - return new MusicGenresValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken); - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresValidator.cs deleted file mode 100644 index 2be99f106..000000000 --- a/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresValidator.cs +++ /dev/null @@ -1,75 +0,0 @@ -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Logging; -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Persistence; - -namespace MediaBrowser.Server.Implementations.Library.Validators -{ - class MusicGenresValidator - { - /// <summary> - /// The _library manager - /// </summary> - private readonly ILibraryManager _libraryManager; - - /// <summary> - /// The _logger - /// </summary> - private readonly ILogger _logger; - private readonly IItemRepository _itemRepo; - - public MusicGenresValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo) - { - _libraryManager = libraryManager; - _logger = logger; - _itemRepo = itemRepo; - } - - /// <summary> - /// Runs the specified progress. - /// </summary> - /// <param name="progress">The progress.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public async Task Run(IProgress<double> progress, CancellationToken cancellationToken) - { - var names = _itemRepo.GetMusicGenreNames(); - - var numComplete = 0; - var count = names.Count; - - foreach (var name in names) - { - try - { - var item = _libraryManager.GetMusicGenre(name); - - await item.RefreshMetadata(cancellationToken).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - // Don't clutter the log - break; - } - catch (Exception ex) - { - _logger.ErrorException("Error refreshing {0}", ex, name); - } - - numComplete++; - double percent = numComplete; - percent /= count; - percent *= 100; - - progress.Report(percent); - } - - progress.Report(100); - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs deleted file mode 100644 index d9a7199be..000000000 --- a/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs +++ /dev/null @@ -1,172 +0,0 @@ -using MediaBrowser.Common.Progress; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Configuration; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.IO; -using MediaBrowser.Model.IO; - -namespace MediaBrowser.Server.Implementations.Library.Validators -{ - /// <summary> - /// Class PeopleValidator - /// </summary> - public class PeopleValidator - { - /// <summary> - /// The _library manager - /// </summary> - private readonly ILibraryManager _libraryManager; - /// <summary> - /// The _logger - /// </summary> - private readonly ILogger _logger; - - private readonly IServerConfigurationManager _config; - private readonly IFileSystem _fileSystem; - - /// <summary> - /// Initializes a new instance of the <see cref="PeopleValidator" /> class. - /// </summary> - /// <param name="libraryManager">The library manager.</param> - /// <param name="logger">The logger.</param> - public PeopleValidator(ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem) - { - _libraryManager = libraryManager; - _logger = logger; - _config = config; - _fileSystem = fileSystem; - } - - private bool DownloadMetadata(PersonInfo i, PeopleMetadataOptions options) - { - if (i.IsType(PersonType.Actor)) - { - return options.DownloadActorMetadata; - } - if (i.IsType(PersonType.Director)) - { - return options.DownloadDirectorMetadata; - } - if (i.IsType(PersonType.Composer)) - { - return options.DownloadComposerMetadata; - } - if (i.IsType(PersonType.Writer)) - { - return options.DownloadWriterMetadata; - } - if (i.IsType(PersonType.Producer)) - { - return options.DownloadProducerMetadata; - } - if (i.IsType(PersonType.GuestStar)) - { - return options.DownloadGuestStarMetadata; - } - - return options.DownloadOtherPeopleMetadata; - } - - /// <summary> - /// Validates the people. - /// </summary> - /// <param name="cancellationToken">The cancellation token.</param> - /// <param name="progress">The progress.</param> - /// <returns>Task.</returns> - public async Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress) - { - var innerProgress = new ActionableProgress<double>(); - - innerProgress.RegisterAction(pct => progress.Report(pct * .15)); - - var peopleOptions = _config.Configuration.PeopleMetadataOptions; - - var people = _libraryManager.GetPeople(new InternalPeopleQuery()); - - var dict = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase); - - foreach (var person in people) - { - var isMetadataEnabled = DownloadMetadata(person, peopleOptions); - - bool currentValue; - if (dict.TryGetValue(person.Name, out currentValue)) - { - if (!currentValue && isMetadataEnabled) - { - dict[person.Name] = true; - } - } - else - { - dict[person.Name] = isMetadataEnabled; - } - } - - var numComplete = 0; - - _logger.Debug("Will refresh {0} people", dict.Count); - - var numPeople = dict.Count; - - foreach (var person in dict) - { - cancellationToken.ThrowIfCancellationRequested(); - - try - { - var item = _libraryManager.GetPerson(person.Key); - - var hasMetdata = !string.IsNullOrWhiteSpace(item.Overview); - var performFullRefresh = !hasMetdata && (DateTime.UtcNow - item.DateLastRefreshed).TotalDays >= 30; - - var defaultMetadataRefreshMode = performFullRefresh - ? MetadataRefreshMode.FullRefresh - : MetadataRefreshMode.Default; - - var imageRefreshMode = performFullRefresh - ? ImageRefreshMode.FullRefresh - : ImageRefreshMode.Default; - - var options = new MetadataRefreshOptions(_fileSystem) - { - MetadataRefreshMode = person.Value ? defaultMetadataRefreshMode : MetadataRefreshMode.ValidationOnly, - ImageRefreshMode = person.Value ? imageRefreshMode : ImageRefreshMode.ValidationOnly, - ForceSave = performFullRefresh - }; - - await item.RefreshMetadata(options, cancellationToken).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - throw; - } - catch (Exception ex) - { - _logger.ErrorException("Error validating IBN entry {0}", ex, person); - } - - // Update progress - numComplete++; - double percent = numComplete; - percent /= numPeople; - - progress.Report(100 * percent); - } - - progress.Report(100); - - _logger.Info("People validation complete"); - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/Validators/StudiosPostScanTask.cs b/MediaBrowser.Server.Implementations/Library/Validators/StudiosPostScanTask.cs deleted file mode 100644 index 77c6d5146..000000000 --- a/MediaBrowser.Server.Implementations/Library/Validators/StudiosPostScanTask.cs +++ /dev/null @@ -1,45 +0,0 @@ -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Logging; -using System; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Controller.Persistence; - -namespace MediaBrowser.Server.Implementations.Library.Validators -{ - /// <summary> - /// Class MusicGenresPostScanTask - /// </summary> - public class StudiosPostScanTask : ILibraryPostScanTask - { - /// <summary> - /// The _library manager - /// </summary> - private readonly ILibraryManager _libraryManager; - - private readonly ILogger _logger; - private readonly IItemRepository _itemRepo; - - /// <summary> - /// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class. - /// </summary> - /// <param name="libraryManager">The library manager.</param> - public StudiosPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo) - { - _libraryManager = libraryManager; - _logger = logger; - _itemRepo = itemRepo; - } - - /// <summary> - /// Runs the specified progress. - /// </summary> - /// <param name="progress">The progress.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public Task Run(IProgress<double> progress, CancellationToken cancellationToken) - { - return new StudiosValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken); - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/Validators/StudiosValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/StudiosValidator.cs deleted file mode 100644 index a19b8158a..000000000 --- a/MediaBrowser.Server.Implementations/Library/Validators/StudiosValidator.cs +++ /dev/null @@ -1,74 +0,0 @@ -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Logging; -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Persistence; - -namespace MediaBrowser.Server.Implementations.Library.Validators -{ - class StudiosValidator - { - /// <summary> - /// The _library manager - /// </summary> - private readonly ILibraryManager _libraryManager; - - private readonly IItemRepository _itemRepo; - /// <summary> - /// The _logger - /// </summary> - private readonly ILogger _logger; - - public StudiosValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo) - { - _libraryManager = libraryManager; - _logger = logger; - _itemRepo = itemRepo; - } - - /// <summary> - /// Runs the specified progress. - /// </summary> - /// <param name="progress">The progress.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public async Task Run(IProgress<double> progress, CancellationToken cancellationToken) - { - var names = _itemRepo.GetStudioNames(); - - var numComplete = 0; - var count = names.Count; - - foreach (var name in names) - { - try - { - var item = _libraryManager.GetStudio(name); - - await item.RefreshMetadata(cancellationToken).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - // Don't clutter the log - break; - } - catch (Exception ex) - { - _logger.ErrorException("Error refreshing {0}", ex, name); - } - - numComplete++; - double percent = numComplete; - percent /= count; - percent *= 100; - - progress.Report(percent); - } - - progress.Report(100); - } - } -} diff --git a/MediaBrowser.Server.Implementations/Library/Validators/YearsPostScanTask.cs b/MediaBrowser.Server.Implementations/Library/Validators/YearsPostScanTask.cs deleted file mode 100644 index 164b14223..000000000 --- a/MediaBrowser.Server.Implementations/Library/Validators/YearsPostScanTask.cs +++ /dev/null @@ -1,55 +0,0 @@ -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Logging; -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Server.Implementations.Library.Validators -{ - public class YearsPostScanTask : ILibraryPostScanTask - { - private readonly ILibraryManager _libraryManager; - private readonly ILogger _logger; - - public YearsPostScanTask(ILibraryManager libraryManager, ILogger logger) - { - _libraryManager = libraryManager; - _logger = logger; - } - - public async Task Run(IProgress<double> progress, CancellationToken cancellationToken) - { - var yearNumber = 1900; - var maxYear = DateTime.UtcNow.Year + 3; - var count = maxYear - yearNumber + 1; - var numComplete = 0; - - while (yearNumber < maxYear) - { - try - { - var year = _libraryManager.GetYear(yearNumber); - - await year.RefreshMetadata(cancellationToken).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - // Don't clutter the log - break; - } - catch (Exception ex) - { - _logger.ErrorException("Error refreshing year {0}", ex, yearNumber); - } - - numComplete++; - double percent = numComplete; - percent /= count; - percent *= 100; - - progress.Report(percent); - yearNumber++; - } - } - } -} diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 397e5fc9f..1e5f760ca 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -15,7 +15,6 @@ using MediaBrowser.Model.Events; using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; -using MediaBrowser.Server.Implementations.FileOrganization; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -1528,28 +1527,28 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV private async void OnSuccessfulRecording(TimerInfo timer, string path) { - if (timer.IsProgramSeries && GetConfiguration().EnableAutoOrganize) - { - try - { - // this is to account for the library monitor holding a lock for additional time after the change is complete. - // ideally this shouldn't be hard-coded - await Task.Delay(30000).ConfigureAwait(false); - - var organize = new EpisodeFileOrganizer(_organizationService, _config, _fileSystem, _logger, _libraryManager, _libraryMonitor, _providerManager); - - var result = await organize.OrganizeEpisodeFile(path, _config.GetAutoOrganizeOptions(), false, CancellationToken.None).ConfigureAwait(false); - - if (result.Status == FileSortingStatus.Success) - { - return; - } - } - catch (Exception ex) - { - _logger.ErrorException("Error processing new recording", ex); - } - } + //if (timer.IsProgramSeries && GetConfiguration().EnableAutoOrganize) + //{ + // try + // { + // // this is to account for the library monitor holding a lock for additional time after the change is complete. + // // ideally this shouldn't be hard-coded + // await Task.Delay(30000).ConfigureAwait(false); + + // var organize = new EpisodeFileOrganizer(_organizationService, _config, _fileSystem, _logger, _libraryManager, _libraryMonitor, _providerManager); + + // var result = await organize.OrganizeEpisodeFile(path, _config.GetAutoOrganizeOptions(), false, CancellationToken.None).ConfigureAwait(false); + + // if (result.Status == FileSortingStatus.Success) + // { + // return; + // } + // } + // catch (Exception ex) + // { + // _logger.ErrorException("Error processing new recording", ex); + // } + //} } private void SaveNfo(TimerInfo timer, string recordingPath, string seriesPath) diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 037c6bd0c..fc71ef8a0 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -108,7 +108,6 @@ <Compile Include="..\SharedVersion.cs"> <Link>Properties\SharedVersion.cs</Link> </Compile> - <Compile Include="Activity\ActivityManager.cs" /> <Compile Include="Activity\ActivityRepository.cs" /> <Compile Include="Archiving\ZipClient.cs" /> <Compile Include="Collections\CollectionsDynamicFolder.cs" /> @@ -122,7 +121,6 @@ <Compile Include="Devices\DeviceManager.cs" /> <Compile Include="Devices\DeviceRepository.cs" /> <Compile Include="Devices\CameraUploadsFolder.cs" /> - <Compile Include="Dto\DtoService.cs" /> <Compile Include="EntryPoints\ActivityLogEntryPoint.cs" /> <Compile Include="EntryPoints\AutomaticRestartEntryPoint.cs" /> <Compile Include="EntryPoints\ExternalPortForwarding.cs" /> @@ -134,16 +132,9 @@ <Compile Include="EntryPoints\RefreshUsersMetadata.cs" /> <Compile Include="EntryPoints\UsageEntryPoint.cs" /> <Compile Include="EntryPoints\UsageReporter.cs" /> - <Compile Include="FileOrganization\EpisodeFileOrganizer.cs" /> - <Compile Include="FileOrganization\Extensions.cs" /> - <Compile Include="FileOrganization\FileOrganizationNotifier.cs" /> - <Compile Include="FileOrganization\FileOrganizationService.cs" /> - <Compile Include="FileOrganization\NameUtils.cs" /> - <Compile Include="FileOrganization\TvFolderOrganizer.cs" /> <Compile Include="EntryPoints\UdpServerEntryPoint.cs" /> <Compile Include="EntryPoints\ServerEventNotifier.cs" /> <Compile Include="EntryPoints\UserDataChangeNotifier.cs" /> - <Compile Include="FileOrganization\OrganizerScheduledTask.cs" /> <Compile Include="HttpServer\IHttpListener.cs" /> <Compile Include="HttpServer\Security\AuthorizationContext.cs" /> <Compile Include="HttpServer\ContainerAdapter.cs" /> @@ -171,45 +162,8 @@ <Compile Include="IO\LibraryMonitor.cs" /> <Compile Include="IO\MemoryStreamProvider.cs" /> <Compile Include="IO\ThrottledStream.cs" /> - <Compile Include="Library\CoreResolutionIgnoreRule.cs" /> - <Compile Include="Library\LibraryManager.cs" /> - <Compile Include="Library\LocalTrailerPostScanTask.cs" /> - <Compile Include="Library\MediaSourceManager.cs" /> - <Compile Include="Library\MusicManager.cs" /> - <Compile Include="Library\PathExtensions.cs" /> - <Compile Include="Library\Resolvers\SpecialFolderResolver.cs" /> - <Compile Include="Library\Resolvers\BaseVideoResolver.cs" /> - <Compile Include="Library\Resolvers\PhotoAlbumResolver.cs" /> - <Compile Include="Library\Resolvers\PhotoResolver.cs" /> - <Compile Include="Library\Resolvers\PlaylistResolver.cs" /> - <Compile Include="Library\SearchEngine.cs" /> - <Compile Include="Library\ResolverHelper.cs" /> - <Compile Include="Library\Resolvers\Audio\AudioResolver.cs" /> - <Compile Include="Library\Resolvers\Audio\MusicAlbumResolver.cs" /> - <Compile Include="Library\Resolvers\Audio\MusicArtistResolver.cs" /> - <Compile Include="Library\Resolvers\ItemResolver.cs" /> - <Compile Include="Library\Resolvers\FolderResolver.cs" /> - <Compile Include="Library\Resolvers\Movies\BoxSetResolver.cs" /> - <Compile Include="Library\Resolvers\Movies\MovieResolver.cs" /> - <Compile Include="Library\Resolvers\TV\EpisodeResolver.cs" /> - <Compile Include="Library\Resolvers\TV\SeasonResolver.cs" /> - <Compile Include="Library\Resolvers\TV\SeriesResolver.cs" /> - <Compile Include="Library\Resolvers\VideoResolver.cs" /> <Compile Include="Library\UserDataManager.cs" /> <Compile Include="Library\UserManager.cs" /> - <Compile Include="Library\UserViewManager.cs" /> - <Compile Include="Library\Validators\ArtistsPostScanTask.cs" /> - <Compile Include="Library\Validators\ArtistsValidator.cs" /> - <Compile Include="Library\Validators\GameGenresPostScanTask.cs" /> - <Compile Include="Library\Validators\GameGenresValidator.cs" /> - <Compile Include="Library\Validators\GenresPostScanTask.cs" /> - <Compile Include="Library\Validators\GenresValidator.cs" /> - <Compile Include="Library\Validators\MusicGenresPostScanTask.cs" /> - <Compile Include="Library\Validators\MusicGenresValidator.cs" /> - <Compile Include="Library\Validators\PeopleValidator.cs" /> - <Compile Include="Library\Validators\StudiosPostScanTask.cs" /> - <Compile Include="Library\Validators\StudiosValidator.cs" /> - <Compile Include="Library\Validators\YearsPostScanTask.cs" /> <Compile Include="LiveTv\ChannelImageProvider.cs" /> <Compile Include="LiveTv\EmbyTV\DirectRecorder.cs" /> <Compile Include="LiveTv\EmbyTV\EmbyTV.cs" /> @@ -267,19 +221,16 @@ <Compile Include="News\NewsEntryPoint.cs" /> <Compile Include="Notifications\IConfigurableNotificationService.cs" /> <Compile Include="Persistence\BaseSqliteRepository.cs" /> - <Compile Include="Persistence\CleanDatabaseScheduledTask.cs" /> <Compile Include="Persistence\DataExtensions.cs" /> <Compile Include="Persistence\IDbConnector.cs" /> <Compile Include="Persistence\MediaStreamColumns.cs" /> <Compile Include="Reflection\AssemblyInfo.cs" /> - <Compile Include="ScheduledTasks\RefreshMediaLibraryTask.cs" /> <Compile Include="Security\MBLicenseFile.cs" /> <Compile Include="Security\PluginSecurityManager.cs" /> <Compile Include="Security\RegRecord.cs" /> <Compile Include="Serialization\JsonSerializer.cs" /> <Compile Include="Social\SharingManager.cs" /> <Compile Include="Social\SharingRepository.cs" /> - <Compile Include="Sorting\StartDateComparer.cs" /> <Compile Include="Sync\SyncHelper.cs" /> <Compile Include="Sync\SyncJobOptions.cs" /> <Compile Include="Sync\SyncNotificationEntryPoint.cs" /> @@ -311,40 +262,10 @@ </Compile> <Compile Include="Session\SessionWebSocketListener.cs" /> <Compile Include="Session\WebSocketController.cs" /> - <Compile Include="Sorting\AiredEpisodeOrderComparer.cs" /> - <Compile Include="Sorting\AirTimeComparer.cs" /> - <Compile Include="Sorting\AlbumArtistComparer.cs" /> - <Compile Include="Sorting\AlbumComparer.cs" /> - <Compile Include="Sorting\AlphanumComparator.cs" /> - <Compile Include="Sorting\ArtistComparer.cs" /> - <Compile Include="Sorting\BudgetComparer.cs" /> - <Compile Include="Sorting\CommunityRatingComparer.cs" /> - <Compile Include="Sorting\CriticRatingComparer.cs" /> - <Compile Include="Sorting\DateCreatedComparer.cs" /> - <Compile Include="Sorting\DateLastMediaAddedComparer.cs" /> - <Compile Include="Sorting\DatePlayedComparer.cs" /> - <Compile Include="Sorting\GameSystemComparer.cs" /> - <Compile Include="Sorting\IsFavoriteOrLikeComparer.cs" /> - <Compile Include="Sorting\IsFolderComparer.cs" /> - <Compile Include="Sorting\IsPlayedComparer.cs" /> - <Compile Include="Sorting\IsUnplayedComparer.cs" /> - <Compile Include="Sorting\MetascoreComparer.cs" /> - <Compile Include="Sorting\NameComparer.cs" /> - <Compile Include="Sorting\OfficialRatingComparer.cs" /> - <Compile Include="Sorting\PlayCountComparer.cs" /> - <Compile Include="Sorting\PlayersComparer.cs" /> - <Compile Include="Sorting\PremiereDateComparer.cs" /> - <Compile Include="Sorting\ProductionYearComparer.cs" /> - <Compile Include="Sorting\RandomComparer.cs" /> - <Compile Include="Sorting\RevenueComparer.cs" /> - <Compile Include="Sorting\RuntimeComparer.cs" /> - <Compile Include="Sorting\SeriesSortNameComparer.cs" /> - <Compile Include="Sorting\SortNameComparer.cs" /> <Compile Include="Persistence\SqliteDisplayPreferencesRepository.cs" /> <Compile Include="Persistence\SqliteItemRepository.cs" /> <Compile Include="Persistence\SqliteUserDataRepository.cs" /> <Compile Include="Persistence\SqliteUserRepository.cs" /> - <Compile Include="Sorting\StudioComparer.cs" /> <Compile Include="Sync\AppSyncProvider.cs" /> <Compile Include="Sync\CloudSyncProfile.cs" /> <Compile Include="Sync\IHasSyncQuality.cs" /> diff --git a/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs b/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs deleted file mode 100644 index ed1d21d9a..000000000 --- a/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs +++ /dev/null @@ -1,360 +0,0 @@ -using MediaBrowser.Common.Progress; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Persistence; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Common.IO; -using MediaBrowser.Model.IO; -using MediaBrowser.Controller.Channels; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.IO; -using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Globalization; -using MediaBrowser.Model.Tasks; -using MediaBrowser.Server.Implementations.ScheduledTasks; - -namespace MediaBrowser.Server.Implementations.Persistence -{ - public class CleanDatabaseScheduledTask : IScheduledTask - { - private readonly ILibraryManager _libraryManager; - private readonly IItemRepository _itemRepo; - private readonly ILogger _logger; - private readonly IServerConfigurationManager _config; - private readonly IFileSystem _fileSystem; - private readonly IHttpServer _httpServer; - private readonly ILocalizationManager _localization; - private readonly ITaskManager _taskManager; - - public const int MigrationVersion = 23; - public static bool EnableUnavailableMessage = false; - - public CleanDatabaseScheduledTask(ILibraryManager libraryManager, IItemRepository itemRepo, ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem, IHttpServer httpServer, ILocalizationManager localization, ITaskManager taskManager) - { - _libraryManager = libraryManager; - _itemRepo = itemRepo; - _logger = logger; - _config = config; - _fileSystem = fileSystem; - _httpServer = httpServer; - _localization = localization; - _taskManager = taskManager; - } - - public string Name - { - get { return "Clean Database"; } - } - - public string Description - { - get { return "Deletes obsolete content from the database."; } - } - - public string Category - { - get { return "Library"; } - } - - public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress) - { - OnProgress(0); - - // Ensure these objects are lazy loaded. - // Without this there is a deadlock that will need to be investigated - var rootChildren = _libraryManager.RootFolder.Children.ToList(); - rootChildren = _libraryManager.GetUserRootFolder().Children.ToList(); - - var innerProgress = new ActionableProgress<double>(); - innerProgress.RegisterAction(p => - { - double newPercentCommplete = .4 * p; - OnProgress(newPercentCommplete); - - progress.Report(newPercentCommplete); - }); - - await UpdateToLatestSchema(cancellationToken, innerProgress).ConfigureAwait(false); - - innerProgress = new ActionableProgress<double>(); - innerProgress.RegisterAction(p => - { - double newPercentCommplete = 40 + .05 * p; - OnProgress(newPercentCommplete); - progress.Report(newPercentCommplete); - }); - await CleanDeadItems(cancellationToken, innerProgress).ConfigureAwait(false); - progress.Report(45); - - innerProgress = new ActionableProgress<double>(); - innerProgress.RegisterAction(p => - { - double newPercentCommplete = 45 + .55 * p; - OnProgress(newPercentCommplete); - progress.Report(newPercentCommplete); - }); - await CleanDeletedItems(cancellationToken, innerProgress).ConfigureAwait(false); - progress.Report(100); - - await _itemRepo.UpdateInheritedValues(cancellationToken).ConfigureAwait(false); - - if (_config.Configuration.MigrationVersion < MigrationVersion) - { - _config.Configuration.MigrationVersion = MigrationVersion; - _config.SaveConfiguration(); - } - - if (_config.Configuration.SchemaVersion < SqliteItemRepository.LatestSchemaVersion) - { - _config.Configuration.SchemaVersion = SqliteItemRepository.LatestSchemaVersion; - _config.SaveConfiguration(); - } - - if (EnableUnavailableMessage) - { - EnableUnavailableMessage = false; - _httpServer.GlobalResponse = null; - _taskManager.QueueScheduledTask<RefreshMediaLibraryTask>(); - } - - _taskManager.SuspendTriggers = false; - } - - private void OnProgress(double newPercentCommplete) - { - if (EnableUnavailableMessage) - { - var html = "<!doctype html><html><head><title>Emby</title></head><body>"; - var text = _localization.GetLocalizedString("DbUpgradeMessage"); - html += string.Format(text, newPercentCommplete.ToString("N2", CultureInfo.InvariantCulture)); - - html += "<script>setTimeout(function(){window.location.reload(true);}, 5000);</script>"; - html += "</body></html>"; - - _httpServer.GlobalResponse = html; - } - } - - private async Task UpdateToLatestSchema(CancellationToken cancellationToken, IProgress<double> progress) - { - var itemIds = _libraryManager.GetItemIds(new InternalItemsQuery - { - IsCurrentSchema = false, - ExcludeItemTypes = new[] { typeof(LiveTvProgram).Name } - }); - - var numComplete = 0; - var numItems = itemIds.Count; - - _logger.Debug("Upgrading schema for {0} items", numItems); - - var list = new List<BaseItem>(); - - foreach (var itemId in itemIds) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (itemId != Guid.Empty) - { - // Somehow some invalid data got into the db. It probably predates the boundary checking - var item = _libraryManager.GetItemById(itemId); - - if (item != null) - { - list.Add(item); - } - } - - if (list.Count >= 1000) - { - try - { - await _itemRepo.SaveItems(list, cancellationToken).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - throw; - } - catch (Exception ex) - { - _logger.ErrorException("Error saving item", ex); - } - - list.Clear(); - } - - numComplete++; - double percent = numComplete; - percent /= numItems; - progress.Report(percent * 100); - } - - if (list.Count > 0) - { - try - { - await _itemRepo.SaveItems(list, cancellationToken).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - throw; - } - catch (Exception ex) - { - _logger.ErrorException("Error saving item", ex); - } - } - - progress.Report(100); - } - - private async Task CleanDeadItems(CancellationToken cancellationToken, IProgress<double> progress) - { - var itemIds = _libraryManager.GetItemIds(new InternalItemsQuery - { - HasDeadParentId = true - }); - - var numComplete = 0; - var numItems = itemIds.Count; - - _logger.Debug("Cleaning {0} items with dead parent links", numItems); - - foreach (var itemId in itemIds) - { - cancellationToken.ThrowIfCancellationRequested(); - - var item = _libraryManager.GetItemById(itemId); - - if (item != null) - { - _logger.Info("Cleaning item {0} type: {1} path: {2}", item.Name, item.GetType().Name, item.Path ?? string.Empty); - - await item.Delete(new DeleteOptions - { - DeleteFileLocation = false - - }).ConfigureAwait(false); - } - - numComplete++; - double percent = numComplete; - percent /= numItems; - progress.Report(percent * 100); - } - - progress.Report(100); - } - - private async Task CleanDeletedItems(CancellationToken cancellationToken, IProgress<double> progress) - { - var result = _itemRepo.GetItemIdsWithPath(new InternalItemsQuery - { - LocationTypes = new[] { LocationType.FileSystem }, - //Limit = limit, - - // These have their own cleanup routines - ExcludeItemTypes = new[] - { - typeof(Person).Name, - typeof(Genre).Name, - typeof(MusicGenre).Name, - typeof(GameGenre).Name, - typeof(Studio).Name, - typeof(Year).Name, - typeof(Channel).Name, - typeof(AggregateFolder).Name, - typeof(CollectionFolder).Name - } - }); - - var numComplete = 0; - var numItems = result.Items.Length; - - foreach (var item in result.Items) - { - cancellationToken.ThrowIfCancellationRequested(); - - var path = item.Item2; - - try - { - if (_fileSystem.FileExists(path) || _fileSystem.DirectoryExists(path)) - { - continue; - } - - var libraryItem = _libraryManager.GetItemById(item.Item1); - - if (libraryItem.IsTopParent) - { - continue; - } - - var hasDualAccess = libraryItem as IHasDualAccess; - if (hasDualAccess != null && hasDualAccess.IsAccessedByName) - { - continue; - } - - var libraryItemPath = libraryItem.Path; - if (!string.Equals(libraryItemPath, path, StringComparison.OrdinalIgnoreCase)) - { - _logger.Error("CleanDeletedItems aborting delete for item {0}-{1} because paths don't match. {2}---{3}", libraryItem.Id, libraryItem.Name, libraryItem.Path ?? string.Empty, path ?? string.Empty); - continue; - } - - if (Folder.IsPathOffline(path)) - { - await libraryItem.UpdateIsOffline(true).ConfigureAwait(false); - continue; - } - - _logger.Info("Deleting item from database {0} because path no longer exists. type: {1} path: {2}", libraryItem.Name, libraryItem.GetType().Name, libraryItemPath ?? string.Empty); - - await libraryItem.OnFileDeleted().ConfigureAwait(false); - } - catch (OperationCanceledException) - { - throw; - } - catch (Exception ex) - { - _logger.ErrorException("Error in CleanDeletedItems. File {0}", ex, path); - } - - numComplete++; - double percent = numComplete; - percent /= numItems; - progress.Report(percent * 100); - } - } - - /// <summary> - /// Creates the triggers that define when the task will run - /// </summary> - /// <returns>IEnumerable{BaseTaskTrigger}.</returns> - public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() - { - return new[] { - - // Every so often - new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks} - }; - } - - public string Key - { - get { return "CleanDatabase"; } - } - } -}
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs deleted file mode 100644 index e695adb54..000000000 --- a/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs +++ /dev/null @@ -1,96 +0,0 @@ -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Library; -using MediaBrowser.Server.Implementations.Library; -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Model.Tasks; - -namespace MediaBrowser.Server.Implementations.ScheduledTasks -{ - /// <summary> - /// Class RefreshMediaLibraryTask - /// </summary> - public class RefreshMediaLibraryTask : IScheduledTask - { - /// <summary> - /// The _library manager - /// </summary> - private readonly ILibraryManager _libraryManager; - private readonly IServerConfigurationManager _config; - - /// <summary> - /// Initializes a new instance of the <see cref="RefreshMediaLibraryTask" /> class. - /// </summary> - /// <param name="libraryManager">The library manager.</param> - public RefreshMediaLibraryTask(ILibraryManager libraryManager, IServerConfigurationManager config) - { - _libraryManager = libraryManager; - _config = config; - } - - /// <summary> - /// Creates the triggers that define when the task will run - /// </summary> - /// <returns>IEnumerable{BaseTaskTrigger}.</returns> - public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() - { - return new[] { - - // Every so often - new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(12).Ticks} - }; - } - - /// <summary> - /// Executes the internal. - /// </summary> - /// <param name="cancellationToken">The cancellation token.</param> - /// <param name="progress">The progress.</param> - /// <returns>Task.</returns> - public Task Execute(CancellationToken cancellationToken, IProgress<double> progress) - { - cancellationToken.ThrowIfCancellationRequested(); - - progress.Report(0); - - return ((LibraryManager)_libraryManager).ValidateMediaLibraryInternal(progress, cancellationToken); - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return "Scan media library"; } - } - - /// <summary> - /// Gets the description. - /// </summary> - /// <value>The description.</value> - public string Description - { - get { return "Scans your media library and refreshes metatata based on configuration."; } - } - - /// <summary> - /// Gets the category. - /// </summary> - /// <value>The category.</value> - public string Category - { - get - { - return "Library"; - } - } - - public string Key - { - get { return "RefreshLibrary"; } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/AirTimeComparer.cs b/MediaBrowser.Server.Implementations/Sorting/AirTimeComparer.cs deleted file mode 100644 index 7e6a252cd..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/AirTimeComparer.cs +++ /dev/null @@ -1,71 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; -using System; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - public class AirTimeComparer : IBaseItemComparer - { - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return DateTime.Compare(GetValue(x), GetValue(y)); - } - - /// <summary> - /// Gets the value. - /// </summary> - /// <param name="x">The x.</param> - /// <returns>System.String.</returns> - private DateTime GetValue(BaseItem x) - { - var series = x as Series; - - if (series == null) - { - var season = x as Season; - - if (season != null) - { - series = season.Series; - } - else - { - var episode = x as Episode; - - if (episode != null) - { - series = episode.Series; - } - } - } - - if (series != null) - { - DateTime result; - if (DateTime.TryParse(series.AirTime, out result)) - { - return result; - } - } - - return DateTime.MinValue; - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.AirTime; } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs b/MediaBrowser.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs deleted file mode 100644 index 91abbe34c..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs +++ /dev/null @@ -1,160 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; -using System; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - class AiredEpisodeOrderComparer : IBaseItemComparer - { - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - if (x.PremiereDate.HasValue && y.PremiereDate.HasValue) - { - var val = DateTime.Compare(x.PremiereDate.Value, y.PremiereDate.Value); - - if (val != 0) - { - //return val; - } - } - - var episode1 = x as Episode; - var episode2 = y as Episode; - - if (episode1 == null) - { - if (episode2 == null) - { - return 0; - } - - return 1; - } - - if (episode2 == null) - { - return -1; - } - - return Compare(episode1, episode2); - } - - private int Compare(Episode x, Episode y) - { - var isXSpecial = (x.ParentIndexNumber ?? -1) == 0; - var isYSpecial = (y.ParentIndexNumber ?? -1) == 0; - - if (isXSpecial && isYSpecial) - { - return CompareSpecials(x, y); - } - - if (!isXSpecial && !isYSpecial) - { - return CompareEpisodes(x, y); - } - - if (!isXSpecial) - { - return CompareEpisodeToSpecial(x, y); - } - - return CompareEpisodeToSpecial(y, x) * -1; - } - - private int CompareEpisodeToSpecial(Episode x, Episode y) - { - // http://thetvdb.com/wiki/index.php?title=Special_Episodes - - var xSeason = x.ParentIndexNumber ?? -1; - var ySeason = y.AirsAfterSeasonNumber ?? y.AirsBeforeSeasonNumber ?? -1; - - if (xSeason != ySeason) - { - return xSeason.CompareTo(ySeason); - } - - // Special comes after episode - if (y.AirsAfterSeasonNumber.HasValue) - { - return -1; - } - - var yEpisode = y.AirsBeforeEpisodeNumber; - - // Special comes before the season - if (!yEpisode.HasValue) - { - return 1; - } - - // Compare episode number - var xEpisode = x.IndexNumber; - - if (!xEpisode.HasValue) - { - // Can't really compare if this happens - return 0; - } - - // Special comes before episode - if (xEpisode.Value == yEpisode.Value) - { - return 1; - } - - return xEpisode.Value.CompareTo(yEpisode.Value); - } - - private int CompareSpecials(Episode x, Episode y) - { - return GetSpecialCompareValue(x).CompareTo(GetSpecialCompareValue(y)); - } - - private int GetSpecialCompareValue(Episode item) - { - // First sort by season number - // Since there are three sort orders, pad with 9 digits (3 for each, figure 1000 episode buffer should be enough) - var val = (item.AirsAfterSeasonNumber ?? item.AirsBeforeSeasonNumber ?? 0) * 1000000000; - - // Second sort order is if it airs after the season - if (item.AirsAfterSeasonNumber.HasValue) - { - val += 1000000; - } - - // Third level is the episode number - val += (item.AirsBeforeEpisodeNumber ?? 0) * 1000; - - // Finally, if that's still the same, last resort is the special number itself - val += item.IndexNumber ?? 0; - - return val; - } - - private int CompareEpisodes(Episode x, Episode y) - { - var xValue = (x.ParentIndexNumber ?? -1) * 1000 + (x.IndexNumber ?? -1); - var yValue = (y.ParentIndexNumber ?? -1) * 1000 + (y.IndexNumber ?? -1); - - return xValue.CompareTo(yValue); - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.AiredEpisodeOrder; } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/AlbumArtistComparer.cs b/MediaBrowser.Server.Implementations/Sorting/AlbumArtistComparer.cs deleted file mode 100644 index 3c79b0c32..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/AlbumArtistComparer.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Linq; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; -using System; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - /// <summary> - /// Class AlbumArtistComparer - /// </summary> - public class AlbumArtistComparer : IBaseItemComparer - { - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return string.Compare(GetValue(x), GetValue(y), StringComparison.CurrentCultureIgnoreCase); - } - - /// <summary> - /// Gets the value. - /// </summary> - /// <param name="x">The x.</param> - /// <returns>System.String.</returns> - private string GetValue(BaseItem x) - { - var audio = x as IHasAlbumArtist; - - return audio != null ? audio.AlbumArtists.FirstOrDefault() : null; - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.AlbumArtist; } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/AlbumComparer.cs b/MediaBrowser.Server.Implementations/Sorting/AlbumComparer.cs deleted file mode 100644 index f455d5c2b..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/AlbumComparer.cs +++ /dev/null @@ -1,46 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; -using System; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - /// <summary> - /// Class AlbumComparer - /// </summary> - public class AlbumComparer : IBaseItemComparer - { - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return string.Compare(GetValue(x), GetValue(y), StringComparison.CurrentCultureIgnoreCase); - } - - /// <summary> - /// Gets the value. - /// </summary> - /// <param name="x">The x.</param> - /// <returns>System.String.</returns> - private string GetValue(BaseItem x) - { - var audio = x as Audio; - - return audio == null ? string.Empty : audio.Album; - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.Album; } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/AlphanumComparator.cs b/MediaBrowser.Server.Implementations/Sorting/AlphanumComparator.cs deleted file mode 100644 index 232bdb3b5..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/AlphanumComparator.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System.Collections.Generic; -using System.Text; -using MediaBrowser.Controller.Sorting; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - public class AlphanumComparator : IComparer<string> - { - public static int CompareValues(string s1, string s2) - { - if (s1 == null || s2 == null) - { - return 0; - } - - int thisMarker = 0, thisNumericChunk = 0; - int thatMarker = 0, thatNumericChunk = 0; - - while ((thisMarker < s1.Length) || (thatMarker < s2.Length)) - { - if (thisMarker >= s1.Length) - { - return -1; - } - else if (thatMarker >= s2.Length) - { - return 1; - } - char thisCh = s1[thisMarker]; - char thatCh = s2[thatMarker]; - - StringBuilder thisChunk = new StringBuilder(); - StringBuilder thatChunk = new StringBuilder(); - - while ((thisMarker < s1.Length) && (thisChunk.Length == 0 || SortHelper.InChunk(thisCh, thisChunk[0]))) - { - thisChunk.Append(thisCh); - thisMarker++; - - if (thisMarker < s1.Length) - { - thisCh = s1[thisMarker]; - } - } - - while ((thatMarker < s2.Length) && (thatChunk.Length == 0 || SortHelper.InChunk(thatCh, thatChunk[0]))) - { - thatChunk.Append(thatCh); - thatMarker++; - - if (thatMarker < s2.Length) - { - thatCh = s2[thatMarker]; - } - } - - int result = 0; - // If both chunks contain numeric characters, sort them numerically - if (char.IsDigit(thisChunk[0]) && char.IsDigit(thatChunk[0])) - { - if (!int.TryParse(thisChunk.ToString(), out thisNumericChunk)) - { - return 0; - } - if (!int.TryParse(thatChunk.ToString(), out thatNumericChunk)) - { - return 0; - } - - if (thisNumericChunk < thatNumericChunk) - { - result = -1; - } - - if (thisNumericChunk > thatNumericChunk) - { - result = 1; - } - } - else - { - result = thisChunk.ToString().CompareTo(thatChunk.ToString()); - } - - if (result != 0) - { - return result; - } - } - - return 0; - } - - public int Compare(string x, string y) - { - return CompareValues(x, y); - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/ArtistComparer.cs b/MediaBrowser.Server.Implementations/Sorting/ArtistComparer.cs deleted file mode 100644 index 9ff8a5ace..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/ArtistComparer.cs +++ /dev/null @@ -1,51 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; -using System; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - /// <summary> - /// Class ArtistComparer - /// </summary> - public class ArtistComparer : IBaseItemComparer - { - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return string.Compare(GetValue(x), GetValue(y), StringComparison.CurrentCultureIgnoreCase); - } - - /// <summary> - /// Gets the value. - /// </summary> - /// <param name="x">The x.</param> - /// <returns>System.String.</returns> - private string GetValue(BaseItem x) - { - var audio = x as Audio; - - if (audio == null) - { - return string.Empty; - } - - return audio.Artists.Count == 0 ? null : audio.Artists[0]; - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.Artist; } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/BudgetComparer.cs b/MediaBrowser.Server.Implementations/Sorting/BudgetComparer.cs deleted file mode 100644 index 87a7325c6..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/BudgetComparer.cs +++ /dev/null @@ -1,39 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - public class BudgetComparer : IBaseItemComparer - { - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return GetValue(x).CompareTo(GetValue(y)); - } - - private double GetValue(BaseItem x) - { - var hasBudget = x as IHasBudget; - if (hasBudget != null) - { - return hasBudget.Budget ?? 0; - } - return 0; - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.Budget; } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/CommunityRatingComparer.cs b/MediaBrowser.Server.Implementations/Sorting/CommunityRatingComparer.cs deleted file mode 100644 index bdd18a648..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/CommunityRatingComparer.cs +++ /dev/null @@ -1,29 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - public class CommunityRatingComparer : IBaseItemComparer - { - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return (x.CommunityRating ?? 0).CompareTo(y.CommunityRating ?? 0); - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.CommunityRating; } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/CriticRatingComparer.cs b/MediaBrowser.Server.Implementations/Sorting/CriticRatingComparer.cs deleted file mode 100644 index 9484130cb..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/CriticRatingComparer.cs +++ /dev/null @@ -1,37 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - /// <summary> - /// Class CriticRatingComparer - /// </summary> - public class CriticRatingComparer : IBaseItemComparer - { - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return GetValue(x).CompareTo(GetValue(y)); - } - - private float GetValue(BaseItem x) - { - return x.CriticRating ?? 0; - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.CriticRating; } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/DateCreatedComparer.cs b/MediaBrowser.Server.Implementations/Sorting/DateCreatedComparer.cs deleted file mode 100644 index 9862f0a8a..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/DateCreatedComparer.cs +++ /dev/null @@ -1,33 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; -using System; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - /// <summary> - /// Class DateCreatedComparer - /// </summary> - public class DateCreatedComparer : IBaseItemComparer - { - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return DateTime.Compare(x.DateCreated, y.DateCreated); - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.DateCreated; } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs b/MediaBrowser.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs deleted file mode 100644 index 5080edffd..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs +++ /dev/null @@ -1,69 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; -using System; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - public class DateLastMediaAddedComparer : IUserBaseItemComparer - { - /// <summary> - /// Gets or sets the user. - /// </summary> - /// <value>The user.</value> - public User User { get; set; } - - /// <summary> - /// Gets or sets the user manager. - /// </summary> - /// <value>The user manager.</value> - public IUserManager UserManager { get; set; } - - /// <summary> - /// Gets or sets the user data repository. - /// </summary> - /// <value>The user data repository.</value> - public IUserDataManager UserDataRepository { get; set; } - - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return GetDate(x).CompareTo(GetDate(y)); - } - - /// <summary> - /// Gets the date. - /// </summary> - /// <param name="x">The x.</param> - /// <returns>DateTime.</returns> - private DateTime GetDate(BaseItem x) - { - var folder = x as Folder; - - if (folder != null) - { - if (folder.DateLastMediaAdded.HasValue) - { - return folder.DateLastMediaAdded.Value; - } - } - - return DateTime.MinValue; - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.DateLastContentAdded; } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/DatePlayedComparer.cs b/MediaBrowser.Server.Implementations/Sorting/DatePlayedComparer.cs deleted file mode 100644 index 3edf23020..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/DatePlayedComparer.cs +++ /dev/null @@ -1,69 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; -using System; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - /// <summary> - /// Class DatePlayedComparer - /// </summary> - public class DatePlayedComparer : IUserBaseItemComparer - { - /// <summary> - /// Gets or sets the user. - /// </summary> - /// <value>The user.</value> - public User User { get; set; } - - /// <summary> - /// Gets or sets the user manager. - /// </summary> - /// <value>The user manager.</value> - public IUserManager UserManager { get; set; } - - /// <summary> - /// Gets or sets the user data repository. - /// </summary> - /// <value>The user data repository.</value> - public IUserDataManager UserDataRepository { get; set; } - - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return GetDate(x).CompareTo(GetDate(y)); - } - - /// <summary> - /// Gets the date. - /// </summary> - /// <param name="x">The x.</param> - /// <returns>DateTime.</returns> - private DateTime GetDate(BaseItem x) - { - var userdata = UserDataRepository.GetUserData(User, x); - - if (userdata != null && userdata.LastPlayedDate.HasValue) - { - return userdata.LastPlayedDate.Value; - } - - return DateTime.MinValue; - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.DatePlayed; } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/GameSystemComparer.cs b/MediaBrowser.Server.Implementations/Sorting/GameSystemComparer.cs deleted file mode 100644 index eb83b98e9..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/GameSystemComparer.cs +++ /dev/null @@ -1,54 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; -using System; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - public class GameSystemComparer : IBaseItemComparer - { - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return string.Compare(GetValue(x), GetValue(y), StringComparison.CurrentCultureIgnoreCase); - } - - /// <summary> - /// Gets the value. - /// </summary> - /// <param name="x">The x.</param> - /// <returns>System.String.</returns> - private string GetValue(BaseItem x) - { - var game = x as Game; - - if (game != null) - { - return game.GameSystem; - } - - var system = x as GameSystem; - - if (system != null) - { - return system.GameSystemName; - } - - return string.Empty; - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.GameSystem; } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/IsFavoriteOrLikeComparer.cs b/MediaBrowser.Server.Implementations/Sorting/IsFavoriteOrLikeComparer.cs deleted file mode 100644 index 658708dba..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/IsFavoriteOrLikeComparer.cs +++ /dev/null @@ -1,58 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - public class IsFavoriteOrLikeComparer : IUserBaseItemComparer - { - /// <summary> - /// Gets or sets the user. - /// </summary> - /// <value>The user.</value> - public User User { get; set; } - - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return GetValue(x).CompareTo(GetValue(y)); - } - - /// <summary> - /// Gets the date. - /// </summary> - /// <param name="x">The x.</param> - /// <returns>DateTime.</returns> - private int GetValue(BaseItem x) - { - return x.IsFavoriteOrLiked(User) ? 0 : 1; - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.IsFavoriteOrLiked; } - } - - /// <summary> - /// Gets or sets the user data repository. - /// </summary> - /// <value>The user data repository.</value> - public IUserDataManager UserDataRepository { get; set; } - - /// <summary> - /// Gets or sets the user manager. - /// </summary> - /// <value>The user manager.</value> - public IUserManager UserManager { get; set; } - } -}
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Sorting/IsFolderComparer.cs b/MediaBrowser.Server.Implementations/Sorting/IsFolderComparer.cs deleted file mode 100644 index d2341d065..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/IsFolderComparer.cs +++ /dev/null @@ -1,39 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - public class IsFolderComparer : IBaseItemComparer - { - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return GetValue(x).CompareTo(GetValue(y)); - } - - /// <summary> - /// Gets the value. - /// </summary> - /// <param name="x">The x.</param> - /// <returns>System.String.</returns> - private int GetValue(BaseItem x) - { - return x.IsFolder ? 0 : 1; - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.IsFolder; } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/IsPlayedComparer.cs b/MediaBrowser.Server.Implementations/Sorting/IsPlayedComparer.cs deleted file mode 100644 index aebfbdb1c..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/IsPlayedComparer.cs +++ /dev/null @@ -1,58 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - public class IsPlayedComparer : IUserBaseItemComparer - { - /// <summary> - /// Gets or sets the user. - /// </summary> - /// <value>The user.</value> - public User User { get; set; } - - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return GetValue(x).CompareTo(GetValue(y)); - } - - /// <summary> - /// Gets the date. - /// </summary> - /// <param name="x">The x.</param> - /// <returns>DateTime.</returns> - private int GetValue(BaseItem x) - { - return x.IsPlayed(User) ? 0 : 1; - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.IsUnplayed; } - } - - /// <summary> - /// Gets or sets the user data repository. - /// </summary> - /// <value>The user data repository.</value> - public IUserDataManager UserDataRepository { get; set; } - - /// <summary> - /// Gets or sets the user manager. - /// </summary> - /// <value>The user manager.</value> - public IUserManager UserManager { get; set; } - } -}
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Sorting/IsUnplayedComparer.cs b/MediaBrowser.Server.Implementations/Sorting/IsUnplayedComparer.cs deleted file mode 100644 index f1c6a5a4e..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/IsUnplayedComparer.cs +++ /dev/null @@ -1,58 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - public class IsUnplayedComparer : IUserBaseItemComparer - { - /// <summary> - /// Gets or sets the user. - /// </summary> - /// <value>The user.</value> - public User User { get; set; } - - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return GetValue(x).CompareTo(GetValue(y)); - } - - /// <summary> - /// Gets the date. - /// </summary> - /// <param name="x">The x.</param> - /// <returns>DateTime.</returns> - private int GetValue(BaseItem x) - { - return x.IsUnplayed(User) ? 0 : 1; - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.IsUnplayed; } - } - - /// <summary> - /// Gets or sets the user data repository. - /// </summary> - /// <value>The user data repository.</value> - public IUserDataManager UserDataRepository { get; set; } - - /// <summary> - /// Gets or sets the user manager. - /// </summary> - /// <value>The user manager.</value> - public IUserManager UserManager { get; set; } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/MetascoreComparer.cs b/MediaBrowser.Server.Implementations/Sorting/MetascoreComparer.cs deleted file mode 100644 index bfd162661..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/MetascoreComparer.cs +++ /dev/null @@ -1,41 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - public class MetascoreComparer : IBaseItemComparer - { - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return GetValue(x).CompareTo(GetValue(y)); - } - - private float GetValue(BaseItem x) - { - var hasMetascore = x as IHasMetascore; - - if (hasMetascore != null) - { - return hasMetascore.Metascore ?? 0; - } - - return 0; - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.Metascore; } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/NameComparer.cs b/MediaBrowser.Server.Implementations/Sorting/NameComparer.cs deleted file mode 100644 index 49f86c485..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/NameComparer.cs +++ /dev/null @@ -1,33 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; -using System; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - /// <summary> - /// Class NameComparer - /// </summary> - public class NameComparer : IBaseItemComparer - { - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return string.Compare(x.Name, y.Name, StringComparison.CurrentCultureIgnoreCase); - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.Name; } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/OfficialRatingComparer.cs b/MediaBrowser.Server.Implementations/Sorting/OfficialRatingComparer.cs deleted file mode 100644 index 3b1939b70..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/OfficialRatingComparer.cs +++ /dev/null @@ -1,40 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Globalization; -using MediaBrowser.Model.Querying; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - public class OfficialRatingComparer : IBaseItemComparer - { - private readonly ILocalizationManager _localization; - - public OfficialRatingComparer(ILocalizationManager localization) - { - _localization = localization; - } - - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - var levelX = string.IsNullOrEmpty(x.OfficialRating) ? 0 : _localization.GetRatingLevel(x.OfficialRating) ?? 0; - var levelY = string.IsNullOrEmpty(y.OfficialRating) ? 0 : _localization.GetRatingLevel(y.OfficialRating) ?? 0; - - return levelX.CompareTo(levelY); - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.OfficialRating; } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/PlayCountComparer.cs b/MediaBrowser.Server.Implementations/Sorting/PlayCountComparer.cs deleted file mode 100644 index 8b14efffc..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/PlayCountComparer.cs +++ /dev/null @@ -1,63 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - /// <summary> - /// Class PlayCountComparer - /// </summary> - public class PlayCountComparer : IUserBaseItemComparer - { - /// <summary> - /// Gets or sets the user. - /// </summary> - /// <value>The user.</value> - public User User { get; set; } - - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return GetValue(x).CompareTo(GetValue(y)); - } - - /// <summary> - /// Gets the date. - /// </summary> - /// <param name="x">The x.</param> - /// <returns>DateTime.</returns> - private int GetValue(BaseItem x) - { - var userdata = UserDataRepository.GetUserData(User, x); - - return userdata == null ? 0 : userdata.PlayCount; - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.PlayCount; } - } - - /// <summary> - /// Gets or sets the user data repository. - /// </summary> - /// <value>The user data repository.</value> - public IUserDataManager UserDataRepository { get; set; } - - /// <summary> - /// Gets or sets the user manager. - /// </summary> - /// <value>The user manager.</value> - public IUserManager UserManager { get; set; } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/PlayersComparer.cs b/MediaBrowser.Server.Implementations/Sorting/PlayersComparer.cs deleted file mode 100644 index 5bcd080d7..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/PlayersComparer.cs +++ /dev/null @@ -1,46 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - public class PlayersComparer : IBaseItemComparer - { - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return GetValue(x).CompareTo(GetValue(y)); - } - - /// <summary> - /// Gets the value. - /// </summary> - /// <param name="x">The x.</param> - /// <returns>System.String.</returns> - private int GetValue(BaseItem x) - { - var game = x as Game; - - if (game != null) - { - return game.PlayersSupported ?? 0; - } - - return 0; - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.Players; } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/PremiereDateComparer.cs b/MediaBrowser.Server.Implementations/Sorting/PremiereDateComparer.cs deleted file mode 100644 index ffe1fc24a..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/PremiereDateComparer.cs +++ /dev/null @@ -1,59 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; -using System; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - /// <summary> - /// Class PremiereDateComparer - /// </summary> - public class PremiereDateComparer : IBaseItemComparer - { - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return GetDate(x).CompareTo(GetDate(y)); - } - - /// <summary> - /// Gets the date. - /// </summary> - /// <param name="x">The x.</param> - /// <returns>DateTime.</returns> - private DateTime GetDate(BaseItem x) - { - if (x.PremiereDate.HasValue) - { - return x.PremiereDate.Value; - } - - if (x.ProductionYear.HasValue) - { - try - { - return new DateTime(x.ProductionYear.Value, 1, 1, 0, 0, 0, DateTimeKind.Utc); - } - catch (ArgumentOutOfRangeException) - { - // Don't blow up if the item has a bad ProductionYear, just return MinValue - } - } - return DateTime.MinValue; - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.PremiereDate; } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/ProductionYearComparer.cs b/MediaBrowser.Server.Implementations/Sorting/ProductionYearComparer.cs deleted file mode 100644 index 16d531334..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/ProductionYearComparer.cs +++ /dev/null @@ -1,52 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - /// <summary> - /// Class ProductionYearComparer - /// </summary> - public class ProductionYearComparer : IBaseItemComparer - { - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return GetValue(x).CompareTo(GetValue(y)); - } - - /// <summary> - /// Gets the date. - /// </summary> - /// <param name="x">The x.</param> - /// <returns>DateTime.</returns> - private int GetValue(BaseItem x) - { - if (x.ProductionYear.HasValue) - { - return x.ProductionYear.Value; - } - - if (x.PremiereDate.HasValue) - { - return x.PremiereDate.Value.Year; - } - - return 0; - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.ProductionYear; } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/RandomComparer.cs b/MediaBrowser.Server.Implementations/Sorting/RandomComparer.cs deleted file mode 100644 index b1677331a..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/RandomComparer.cs +++ /dev/null @@ -1,33 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; -using System; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - /// <summary> - /// Class RandomComparer - /// </summary> - public class RandomComparer : IBaseItemComparer - { - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return Guid.NewGuid().CompareTo(Guid.NewGuid()); - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.Random; } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/RevenueComparer.cs b/MediaBrowser.Server.Implementations/Sorting/RevenueComparer.cs deleted file mode 100644 index 6caa27ac3..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/RevenueComparer.cs +++ /dev/null @@ -1,39 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - public class RevenueComparer : IBaseItemComparer - { - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return GetValue(x).CompareTo(GetValue(y)); - } - - private double GetValue(BaseItem x) - { - var hasBudget = x as IHasBudget; - if (hasBudget != null) - { - return hasBudget.Revenue ?? 0; - } - return 0; - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.Revenue; } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/RuntimeComparer.cs b/MediaBrowser.Server.Implementations/Sorting/RuntimeComparer.cs deleted file mode 100644 index 793cb265e..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/RuntimeComparer.cs +++ /dev/null @@ -1,32 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - /// <summary> - /// Class RuntimeComparer - /// </summary> - public class RuntimeComparer : IBaseItemComparer - { - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return (x.RunTimeTicks ?? 0).CompareTo(y.RunTimeTicks ?? 0); - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.Runtime; } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/SeriesSortNameComparer.cs b/MediaBrowser.Server.Implementations/Sorting/SeriesSortNameComparer.cs deleted file mode 100644 index 6bc1264a4..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/SeriesSortNameComparer.cs +++ /dev/null @@ -1,37 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; -using System; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - class SeriesSortNameComparer : IBaseItemComparer - { - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return string.Compare(GetValue(x), GetValue(y), StringComparison.CurrentCultureIgnoreCase); - } - - private string GetValue(BaseItem item) - { - var hasSeries = item as IHasSeries; - - return hasSeries != null ? hasSeries.SeriesSortName : null; - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.SeriesSortName; } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/SortNameComparer.cs b/MediaBrowser.Server.Implementations/Sorting/SortNameComparer.cs deleted file mode 100644 index 873753a2b..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/SortNameComparer.cs +++ /dev/null @@ -1,33 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; -using System; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - /// <summary> - /// Class SortNameComparer - /// </summary> - public class SortNameComparer : IBaseItemComparer - { - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return string.Compare(x.SortName, y.SortName, StringComparison.CurrentCultureIgnoreCase); - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.SortName; } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/StartDateComparer.cs b/MediaBrowser.Server.Implementations/Sorting/StartDateComparer.cs deleted file mode 100644 index 7e6f24ec1..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/StartDateComparer.cs +++ /dev/null @@ -1,47 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; -using System; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - public class StartDateComparer : IBaseItemComparer - { - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return GetDate(x).CompareTo(GetDate(y)); - } - - /// <summary> - /// Gets the date. - /// </summary> - /// <param name="x">The x.</param> - /// <returns>DateTime.</returns> - private DateTime GetDate(BaseItem x) - { - var hasStartDate = x as LiveTvProgram; - - if (hasStartDate != null) - { - return hasStartDate.StartDate; - } - return DateTime.MinValue; - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.StartDate; } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sorting/StudioComparer.cs b/MediaBrowser.Server.Implementations/Sorting/StudioComparer.cs deleted file mode 100644 index 83ab4dfc2..000000000 --- a/MediaBrowser.Server.Implementations/Sorting/StudioComparer.cs +++ /dev/null @@ -1,30 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; -using System.Linq; - -namespace MediaBrowser.Server.Implementations.Sorting -{ - public class StudioComparer : IBaseItemComparer - { - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return AlphanumComparator.CompareValues(x.Studios.FirstOrDefault() ?? string.Empty, y.Studios.FirstOrDefault() ?? string.Empty); - } - - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - public string Name - { - get { return ItemSortBy.Studio; } - } - } -} |
