mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-06-22 15:40:45 +01:00
move additional classes to new server lib
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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[] { };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user