commit
0d446c8755
|
@ -77,7 +77,7 @@ namespace Emby.Server.Implementations.Library
|
||||||
if (parent != null)
|
if (parent != null)
|
||||||
{
|
{
|
||||||
// Don't resolve these into audio files
|
// Don't resolve these into audio files
|
||||||
if (string.Equals(Path.GetFileNameWithoutExtension(filename), BaseItem.ThemeSongFilename)
|
if (string.Equals(Path.GetFileNameWithoutExtension(filename), BaseItem.ThemeSongFilename, StringComparison.Ordinal)
|
||||||
&& _libraryManager.IsAudioFile(filename))
|
&& _libraryManager.IsAudioFile(filename))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -11,6 +11,17 @@ namespace Emby.Server.Implementations.Library
|
||||||
{
|
{
|
||||||
public class ExclusiveLiveStream : ILiveStream
|
public class ExclusiveLiveStream : ILiveStream
|
||||||
{
|
{
|
||||||
|
private readonly Func<Task> _closeFn;
|
||||||
|
|
||||||
|
public ExclusiveLiveStream(MediaSourceInfo mediaSource, Func<Task> closeFn)
|
||||||
|
{
|
||||||
|
MediaSource = mediaSource;
|
||||||
|
EnableStreamSharing = false;
|
||||||
|
_closeFn = closeFn;
|
||||||
|
ConsumerCount = 1;
|
||||||
|
UniqueId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
public int ConsumerCount { get; set; }
|
public int ConsumerCount { get; set; }
|
||||||
|
|
||||||
public string OriginalStreamId { get; set; }
|
public string OriginalStreamId { get; set; }
|
||||||
|
@ -21,18 +32,7 @@ namespace Emby.Server.Implementations.Library
|
||||||
|
|
||||||
public MediaSourceInfo MediaSource { get; set; }
|
public MediaSourceInfo MediaSource { get; set; }
|
||||||
|
|
||||||
public string UniqueId { get; private set; }
|
public string UniqueId { get; }
|
||||||
|
|
||||||
private Func<Task> _closeFn;
|
|
||||||
|
|
||||||
public ExclusiveLiveStream(MediaSourceInfo mediaSource, Func<Task> closeFn)
|
|
||||||
{
|
|
||||||
MediaSource = mediaSource;
|
|
||||||
EnableStreamSharing = false;
|
|
||||||
_closeFn = closeFn;
|
|
||||||
ConsumerCount = 1;
|
|
||||||
UniqueId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task Close()
|
public Task Close()
|
||||||
{
|
{
|
||||||
|
|
|
@ -60,6 +60,8 @@ namespace Emby.Server.Implementations.Library
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class LibraryManager : ILibraryManager
|
public class LibraryManager : ILibraryManager
|
||||||
{
|
{
|
||||||
|
private const string ShortcutFileExtension = ".mblink";
|
||||||
|
|
||||||
private readonly ILogger<LibraryManager> _logger;
|
private readonly ILogger<LibraryManager> _logger;
|
||||||
private readonly ITaskManager _taskManager;
|
private readonly ITaskManager _taskManager;
|
||||||
private readonly IUserManager _userManager;
|
private readonly IUserManager _userManager;
|
||||||
|
@ -75,63 +77,24 @@ namespace Emby.Server.Implementations.Library
|
||||||
private readonly ConcurrentDictionary<Guid, BaseItem> _libraryItemsCache;
|
private readonly ConcurrentDictionary<Guid, BaseItem> _libraryItemsCache;
|
||||||
private readonly IImageProcessor _imageProcessor;
|
private readonly IImageProcessor _imageProcessor;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The _root folder sync lock.
|
||||||
|
/// </summary>
|
||||||
|
private readonly object _rootFolderSyncLock = new object();
|
||||||
|
private readonly object _userRootFolderSyncLock = new object();
|
||||||
|
|
||||||
|
private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromHours(24);
|
||||||
|
|
||||||
private NamingOptions _namingOptions;
|
private NamingOptions _namingOptions;
|
||||||
private string[] _videoFileExtensions;
|
private string[] _videoFileExtensions;
|
||||||
|
|
||||||
private ILibraryMonitor LibraryMonitor => _libraryMonitorFactory.Value;
|
|
||||||
|
|
||||||
private IProviderManager ProviderManager => _providerManagerFactory.Value;
|
|
||||||
|
|
||||||
private IUserViewManager UserViewManager => _userviewManagerFactory.Value;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the postscan tasks.
|
/// The _root folder.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The postscan tasks.</value>
|
private volatile AggregateFolder _rootFolder;
|
||||||
private ILibraryPostScanTask[] PostscanTasks { get; set; }
|
private volatile UserRootFolder _userRootFolder;
|
||||||
|
|
||||||
/// <summary>
|
private bool _wizardCompleted;
|
||||||
/// Gets or sets the intro providers.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The intro providers.</value>
|
|
||||||
private IIntroProvider[] IntroProviders { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the list of entity resolution ignore rules.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The entity resolution ignore rules.</value>
|
|
||||||
private IResolverIgnoreRule[] EntityResolutionIgnoreRules { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the list of currently registered entity resolvers.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The entity resolvers enumerable.</value>
|
|
||||||
private IItemResolver[] EntityResolvers { get; set; }
|
|
||||||
|
|
||||||
private IMultiItemResolver[] MultiItemResolvers { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the comparers.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The comparers.</value>
|
|
||||||
private IBaseItemComparer[] Comparers { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when [item added].
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<ItemChangeEventArgs> ItemAdded;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when [item updated].
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<ItemChangeEventArgs> ItemUpdated;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when [item removed].
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<ItemChangeEventArgs> ItemRemoved;
|
|
||||||
|
|
||||||
public bool IsScanRunning { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="LibraryManager" /> class.
|
/// Initializes a new instance of the <see cref="LibraryManager" /> class.
|
||||||
|
@ -186,37 +149,19 @@ namespace Emby.Server.Implementations.Library
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the parts.
|
/// Occurs when [item added].
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="rules">The rules.</param>
|
public event EventHandler<ItemChangeEventArgs> ItemAdded;
|
||||||
/// <param name="resolvers">The resolvers.</param>
|
|
||||||
/// <param name="introProviders">The intro providers.</param>
|
|
||||||
/// <param name="itemComparers">The item comparers.</param>
|
|
||||||
/// <param name="postscanTasks">The post scan tasks.</param>
|
|
||||||
public void AddParts(
|
|
||||||
IEnumerable<IResolverIgnoreRule> rules,
|
|
||||||
IEnumerable<IItemResolver> resolvers,
|
|
||||||
IEnumerable<IIntroProvider> introProviders,
|
|
||||||
IEnumerable<IBaseItemComparer> itemComparers,
|
|
||||||
IEnumerable<ILibraryPostScanTask> postscanTasks)
|
|
||||||
{
|
|
||||||
EntityResolutionIgnoreRules = rules.ToArray();
|
|
||||||
EntityResolvers = resolvers.OrderBy(i => i.Priority).ToArray();
|
|
||||||
MultiItemResolvers = EntityResolvers.OfType<IMultiItemResolver>().ToArray();
|
|
||||||
IntroProviders = introProviders.ToArray();
|
|
||||||
Comparers = itemComparers.ToArray();
|
|
||||||
PostscanTasks = postscanTasks.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The _root folder.
|
/// Occurs when [item updated].
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private volatile AggregateFolder _rootFolder;
|
public event EventHandler<ItemChangeEventArgs> ItemUpdated;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The _root folder sync lock.
|
/// Occurs when [item removed].
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly object _rootFolderSyncLock = new object();
|
public event EventHandler<ItemChangeEventArgs> ItemRemoved;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the root folder.
|
/// Gets the root folder.
|
||||||
|
@ -241,7 +186,68 @@ namespace Emby.Server.Implementations.Library
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool _wizardCompleted;
|
private ILibraryMonitor LibraryMonitor => _libraryMonitorFactory.Value;
|
||||||
|
|
||||||
|
private IProviderManager ProviderManager => _providerManagerFactory.Value;
|
||||||
|
|
||||||
|
private IUserViewManager UserViewManager => _userviewManagerFactory.Value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the postscan tasks.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The postscan tasks.</value>
|
||||||
|
private ILibraryPostScanTask[] PostscanTasks { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the intro providers.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The intro providers.</value>
|
||||||
|
private IIntroProvider[] IntroProviders { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the list of entity resolution ignore rules.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The entity resolution ignore rules.</value>
|
||||||
|
private IResolverIgnoreRule[] EntityResolutionIgnoreRules { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the list of currently registered entity resolvers.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The entity resolvers enumerable.</value>
|
||||||
|
private IItemResolver[] EntityResolvers { get; set; }
|
||||||
|
|
||||||
|
private IMultiItemResolver[] MultiItemResolvers { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the comparers.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The comparers.</value>
|
||||||
|
private IBaseItemComparer[] Comparers { get; set; }
|
||||||
|
|
||||||
|
public bool IsScanRunning { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the parts.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rules">The rules.</param>
|
||||||
|
/// <param name="resolvers">The resolvers.</param>
|
||||||
|
/// <param name="introProviders">The intro providers.</param>
|
||||||
|
/// <param name="itemComparers">The item comparers.</param>
|
||||||
|
/// <param name="postscanTasks">The post scan tasks.</param>
|
||||||
|
public void AddParts(
|
||||||
|
IEnumerable<IResolverIgnoreRule> rules,
|
||||||
|
IEnumerable<IItemResolver> resolvers,
|
||||||
|
IEnumerable<IIntroProvider> introProviders,
|
||||||
|
IEnumerable<IBaseItemComparer> itemComparers,
|
||||||
|
IEnumerable<ILibraryPostScanTask> postscanTasks)
|
||||||
|
{
|
||||||
|
EntityResolutionIgnoreRules = rules.ToArray();
|
||||||
|
EntityResolvers = resolvers.OrderBy(i => i.Priority).ToArray();
|
||||||
|
MultiItemResolvers = EntityResolvers.OfType<IMultiItemResolver>().ToArray();
|
||||||
|
IntroProviders = introProviders.ToArray();
|
||||||
|
Comparers = itemComparers.ToArray();
|
||||||
|
PostscanTasks = postscanTasks.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Records the configuration values.
|
/// Records the configuration values.
|
||||||
|
@ -512,7 +518,7 @@ namespace Emby.Server.Implementations.Library
|
||||||
// Try to normalize paths located underneath program-data in an attempt to make them more portable
|
// Try to normalize paths located underneath program-data in an attempt to make them more portable
|
||||||
key = key.Substring(_configurationManager.ApplicationPaths.ProgramDataPath.Length)
|
key = key.Substring(_configurationManager.ApplicationPaths.ProgramDataPath.Length)
|
||||||
.TrimStart(new[] { '/', '\\' })
|
.TrimStart(new[] { '/', '\\' })
|
||||||
.Replace("/", "\\");
|
.Replace('/', '\\');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (forceCaseInsensitive || !_configurationManager.Configuration.EnableCaseSensitiveItemIds)
|
if (forceCaseInsensitive || !_configurationManager.Configuration.EnableCaseSensitiveItemIds)
|
||||||
|
@ -775,14 +781,11 @@ namespace Emby.Server.Implementations.Library
|
||||||
return rootFolder;
|
return rootFolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
private volatile UserRootFolder _userRootFolder;
|
|
||||||
private readonly object _syncLock = new object();
|
|
||||||
|
|
||||||
public Folder GetUserRootFolder()
|
public Folder GetUserRootFolder()
|
||||||
{
|
{
|
||||||
if (_userRootFolder == null)
|
if (_userRootFolder == null)
|
||||||
{
|
{
|
||||||
lock (_syncLock)
|
lock (_userRootFolderSyncLock)
|
||||||
{
|
{
|
||||||
if (_userRootFolder == null)
|
if (_userRootFolder == null)
|
||||||
{
|
{
|
||||||
|
@ -1332,7 +1335,7 @@ namespace Emby.Server.Implementations.Library
|
||||||
|
|
||||||
return new QueryResult<BaseItem>
|
return new QueryResult<BaseItem>
|
||||||
{
|
{
|
||||||
Items = _itemRepository.GetItemList(query).ToArray()
|
Items = _itemRepository.GetItemList(query)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1463,11 +1466,9 @@ namespace Emby.Server.Implementations.Library
|
||||||
return _itemRepository.GetItems(query);
|
return _itemRepository.GetItems(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
var list = _itemRepository.GetItemList(query);
|
|
||||||
|
|
||||||
return new QueryResult<BaseItem>
|
return new QueryResult<BaseItem>
|
||||||
{
|
{
|
||||||
Items = list
|
Items = _itemRepository.GetItemList(query)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1946,12 +1947,9 @@ namespace Emby.Server.Implementations.Library
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the item.
|
/// Updates the item.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void UpdateItems(IEnumerable<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
|
public void UpdateItems(IReadOnlyList<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// Don't iterate multiple times
|
foreach (var item in items)
|
||||||
var itemsList = items.ToList();
|
|
||||||
|
|
||||||
foreach (var item in itemsList)
|
|
||||||
{
|
{
|
||||||
if (item.IsFileProtocol)
|
if (item.IsFileProtocol)
|
||||||
{
|
{
|
||||||
|
@ -1963,11 +1961,11 @@ namespace Emby.Server.Implementations.Library
|
||||||
UpdateImages(item, updateReason >= ItemUpdateType.ImageUpdate);
|
UpdateImages(item, updateReason >= ItemUpdateType.ImageUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
_itemRepository.SaveItems(itemsList, cancellationToken);
|
_itemRepository.SaveItems(items, cancellationToken);
|
||||||
|
|
||||||
if (ItemUpdated != null)
|
if (ItemUpdated != null)
|
||||||
{
|
{
|
||||||
foreach (var item in itemsList)
|
foreach (var item in items)
|
||||||
{
|
{
|
||||||
// With the live tv guide this just creates too much noise
|
// With the live tv guide this just creates too much noise
|
||||||
if (item.SourceType != SourceType.Library)
|
if (item.SourceType != SourceType.Library)
|
||||||
|
@ -2190,8 +2188,6 @@ namespace Emby.Server.Implementations.Library
|
||||||
.FirstOrDefault(i => !string.IsNullOrEmpty(i));
|
.FirstOrDefault(i => !string.IsNullOrEmpty(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromHours(24);
|
|
||||||
|
|
||||||
public UserView GetNamedView(
|
public UserView GetNamedView(
|
||||||
User user,
|
User user,
|
||||||
string name,
|
string name,
|
||||||
|
@ -2489,14 +2485,9 @@ namespace Emby.Server.Implementations.Library
|
||||||
|
|
||||||
var isFolder = episode.VideoType == VideoType.BluRay || episode.VideoType == VideoType.Dvd;
|
var isFolder = episode.VideoType == VideoType.BluRay || episode.VideoType == VideoType.Dvd;
|
||||||
|
|
||||||
var episodeInfo = episode.IsFileProtocol ?
|
var episodeInfo = episode.IsFileProtocol
|
||||||
resolver.Resolve(episode.Path, isFolder, null, null, isAbsoluteNaming) :
|
? resolver.Resolve(episode.Path, isFolder, null, null, isAbsoluteNaming) ?? new Naming.TV.EpisodeInfo()
|
||||||
new Naming.TV.EpisodeInfo();
|
: new Naming.TV.EpisodeInfo();
|
||||||
|
|
||||||
if (episodeInfo == null)
|
|
||||||
{
|
|
||||||
episodeInfo = new Naming.TV.EpisodeInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -2504,11 +2495,13 @@ namespace Emby.Server.Implementations.Library
|
||||||
if (libraryOptions.EnableEmbeddedEpisodeInfos && string.Equals(episodeInfo.Container, "mp4", StringComparison.OrdinalIgnoreCase))
|
if (libraryOptions.EnableEmbeddedEpisodeInfos && string.Equals(episodeInfo.Container, "mp4", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
// Read from metadata
|
// Read from metadata
|
||||||
var mediaInfo = _mediaEncoder.GetMediaInfo(new MediaInfoRequest
|
var mediaInfo = _mediaEncoder.GetMediaInfo(
|
||||||
{
|
new MediaInfoRequest
|
||||||
MediaSource = episode.GetMediaSources(false)[0],
|
{
|
||||||
MediaType = DlnaProfileType.Video
|
MediaSource = episode.GetMediaSources(false)[0],
|
||||||
}, CancellationToken.None).GetAwaiter().GetResult();
|
MediaType = DlnaProfileType.Video
|
||||||
|
},
|
||||||
|
CancellationToken.None).GetAwaiter().GetResult();
|
||||||
if (mediaInfo.ParentIndexNumber > 0)
|
if (mediaInfo.ParentIndexNumber > 0)
|
||||||
{
|
{
|
||||||
episodeInfo.SeasonNumber = mediaInfo.ParentIndexNumber;
|
episodeInfo.SeasonNumber = mediaInfo.ParentIndexNumber;
|
||||||
|
@ -2666,7 +2659,7 @@ namespace Emby.Server.Implementations.Library
|
||||||
|
|
||||||
var videos = videoListResolver.Resolve(fileSystemChildren);
|
var videos = videoListResolver.Resolve(fileSystemChildren);
|
||||||
|
|
||||||
var currentVideo = videos.FirstOrDefault(i => string.Equals(owner.Path, i.Files.First().Path, StringComparison.OrdinalIgnoreCase));
|
var currentVideo = videos.FirstOrDefault(i => string.Equals(owner.Path, i.Files[0].Path, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
if (currentVideo != null)
|
if (currentVideo != null)
|
||||||
{
|
{
|
||||||
|
@ -2683,9 +2676,7 @@ namespace Emby.Server.Implementations.Library
|
||||||
.Select(video =>
|
.Select(video =>
|
||||||
{
|
{
|
||||||
// Try to retrieve it from the db. If we don't find it, use the resolved version
|
// Try to retrieve it from the db. If we don't find it, use the resolved version
|
||||||
var dbItem = GetItemById(video.Id) as Trailer;
|
if (GetItemById(video.Id) is Trailer dbItem)
|
||||||
|
|
||||||
if (dbItem != null)
|
|
||||||
{
|
{
|
||||||
video = dbItem;
|
video = dbItem;
|
||||||
}
|
}
|
||||||
|
@ -3012,8 +3003,6 @@ namespace Emby.Server.Implementations.Library
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private const string ShortcutFileExtension = ".mblink";
|
|
||||||
|
|
||||||
public void AddMediaPath(string virtualFolderName, MediaPathInfo pathInfo)
|
public void AddMediaPath(string virtualFolderName, MediaPathInfo pathInfo)
|
||||||
{
|
{
|
||||||
AddMediaPathInternal(virtualFolderName, pathInfo, true);
|
AddMediaPathInternal(virtualFolderName, pathInfo, true);
|
||||||
|
@ -3207,7 +3196,8 @@ namespace Emby.Server.Implementations.Library
|
||||||
|
|
||||||
if (!Directory.Exists(virtualFolderPath))
|
if (!Directory.Exists(virtualFolderPath))
|
||||||
{
|
{
|
||||||
throw new FileNotFoundException(string.Format("The media collection {0} does not exist", virtualFolderName));
|
throw new FileNotFoundException(
|
||||||
|
string.Format(CultureInfo.InvariantCulture, "The media collection {0} does not exist", virtualFolderName));
|
||||||
}
|
}
|
||||||
|
|
||||||
var shortcut = _fileSystem.GetFilePaths(virtualFolderPath, true)
|
var shortcut = _fileSystem.GetFilePaths(virtualFolderPath, true)
|
||||||
|
|
|
@ -23,9 +23,8 @@ namespace Emby.Server.Implementations.Library
|
||||||
{
|
{
|
||||||
private readonly IMediaEncoder _mediaEncoder;
|
private readonly IMediaEncoder _mediaEncoder;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
private readonly IJsonSerializer _json;
|
||||||
private IJsonSerializer _json;
|
private readonly IApplicationPaths _appPaths;
|
||||||
private IApplicationPaths _appPaths;
|
|
||||||
|
|
||||||
public LiveStreamHelper(IMediaEncoder mediaEncoder, ILogger logger, IJsonSerializer json, IApplicationPaths appPaths)
|
public LiveStreamHelper(IMediaEncoder mediaEncoder, ILogger logger, IJsonSerializer json, IApplicationPaths appPaths)
|
||||||
{
|
{
|
||||||
|
@ -72,13 +71,14 @@ namespace Emby.Server.Implementations.Library
|
||||||
|
|
||||||
mediaSource.AnalyzeDurationMs = 3000;
|
mediaSource.AnalyzeDurationMs = 3000;
|
||||||
|
|
||||||
mediaInfo = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest
|
mediaInfo = await _mediaEncoder.GetMediaInfo(
|
||||||
{
|
new MediaInfoRequest
|
||||||
MediaSource = mediaSource,
|
{
|
||||||
MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video,
|
MediaSource = mediaSource,
|
||||||
ExtractChapters = false
|
MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video,
|
||||||
|
ExtractChapters = false
|
||||||
}, cancellationToken).ConfigureAwait(false);
|
},
|
||||||
|
cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
if (cacheFilePath != null)
|
if (cacheFilePath != null)
|
||||||
{
|
{
|
||||||
|
@ -126,7 +126,7 @@ namespace Emby.Server.Implementations.Library
|
||||||
mediaSource.RunTimeTicks = null;
|
mediaSource.RunTimeTicks = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaBrowser.Model.Entities.MediaStreamType.Audio);
|
var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
|
||||||
|
|
||||||
if (audioStream == null || audioStream.Index == -1)
|
if (audioStream == null || audioStream.Index == -1)
|
||||||
{
|
{
|
||||||
|
@ -137,7 +137,7 @@ namespace Emby.Server.Implementations.Library
|
||||||
mediaSource.DefaultAudioStreamIndex = audioStream.Index;
|
mediaSource.DefaultAudioStreamIndex = audioStream.Index;
|
||||||
}
|
}
|
||||||
|
|
||||||
var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaBrowser.Model.Entities.MediaStreamType.Video);
|
var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
|
||||||
if (videoStream != null)
|
if (videoStream != null)
|
||||||
{
|
{
|
||||||
if (!videoStream.BitRate.HasValue)
|
if (!videoStream.BitRate.HasValue)
|
||||||
|
|
|
@ -29,6 +29,9 @@ namespace Emby.Server.Implementations.Library
|
||||||
{
|
{
|
||||||
public class MediaSourceManager : IMediaSourceManager, IDisposable
|
public class MediaSourceManager : IMediaSourceManager, IDisposable
|
||||||
{
|
{
|
||||||
|
// 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 readonly IItemRepository _itemRepo;
|
private readonly IItemRepository _itemRepo;
|
||||||
private readonly IUserManager _userManager;
|
private readonly IUserManager _userManager;
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
|
@ -40,6 +43,11 @@ namespace Emby.Server.Implementations.Library
|
||||||
private readonly ILocalizationManager _localizationManager;
|
private readonly ILocalizationManager _localizationManager;
|
||||||
private readonly IApplicationPaths _appPaths;
|
private readonly IApplicationPaths _appPaths;
|
||||||
|
|
||||||
|
private readonly Dictionary<string, ILiveStream> _openStreams = new Dictionary<string, ILiveStream>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1);
|
||||||
|
|
||||||
|
private readonly object _disposeLock = new object();
|
||||||
|
|
||||||
private IMediaSourceProvider[] _providers;
|
private IMediaSourceProvider[] _providers;
|
||||||
|
|
||||||
public MediaSourceManager(
|
public MediaSourceManager(
|
||||||
|
@ -368,7 +376,6 @@ namespace Emby.Server.Implementations.Library
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var preferredSubs = string.IsNullOrEmpty(user.SubtitleLanguagePreference)
|
var preferredSubs = string.IsNullOrEmpty(user.SubtitleLanguagePreference)
|
||||||
? Array.Empty<string>() : NormalizeLanguage(user.SubtitleLanguagePreference);
|
? Array.Empty<string>() : NormalizeLanguage(user.SubtitleLanguagePreference);
|
||||||
|
|
||||||
|
@ -451,9 +458,6 @@ namespace Emby.Server.Implementations.Library
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly Dictionary<string, ILiveStream> _openStreams = new Dictionary<string, ILiveStream>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1);
|
|
||||||
|
|
||||||
public async Task<Tuple<LiveStreamResponse, IDirectStreamProvider>> OpenLiveStreamInternal(LiveStreamRequest request, CancellationToken cancellationToken)
|
public async Task<Tuple<LiveStreamResponse, IDirectStreamProvider>> OpenLiveStreamInternal(LiveStreamRequest request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
@ -855,9 +859,6 @@ namespace Emby.Server.Implementations.Library
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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)
|
private Tuple<IMediaSourceProvider, string> GetProvider(string key)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(key))
|
if (string.IsNullOrEmpty(key))
|
||||||
|
@ -881,9 +882,9 @@ namespace Emby.Server.Implementations.Library
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Dispose(true);
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly object _disposeLock = new object();
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Releases unmanaged and - optionally - managed resources.
|
/// Releases unmanaged and - optionally - managed resources.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -89,7 +89,7 @@ namespace Emby.Server.Implementations.Library
|
||||||
}
|
}
|
||||||
|
|
||||||
// load forced subs if we have found no suitable full subtitles
|
// load forced subs if we have found no suitable full subtitles
|
||||||
stream = stream ?? streams.FirstOrDefault(s => s.IsForced && string.Equals(s.Language, audioTrackLanguage, StringComparison.OrdinalIgnoreCase));
|
stream ??= streams.FirstOrDefault(s => s.IsForced && string.Equals(s.Language, audioTrackLanguage, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
if (stream != null)
|
if (stream != null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -20,13 +20,11 @@ namespace Emby.Server.Implementations.Library
|
||||||
{
|
{
|
||||||
public class SearchEngine : ISearchEngine
|
public class SearchEngine : ISearchEngine
|
||||||
{
|
{
|
||||||
private readonly ILogger<SearchEngine> _logger;
|
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
private readonly IUserManager _userManager;
|
private readonly IUserManager _userManager;
|
||||||
|
|
||||||
public SearchEngine(ILogger<SearchEngine> logger, ILibraryManager libraryManager, IUserManager userManager)
|
public SearchEngine(ILibraryManager libraryManager, IUserManager userManager)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
}
|
}
|
||||||
|
@ -34,11 +32,7 @@ namespace Emby.Server.Implementations.Library
|
||||||
public QueryResult<SearchHintInfo> GetSearchHints(SearchQuery query)
|
public QueryResult<SearchHintInfo> GetSearchHints(SearchQuery query)
|
||||||
{
|
{
|
||||||
User user = null;
|
User user = null;
|
||||||
|
if (query.UserId != Guid.Empty)
|
||||||
if (query.UserId.Equals(Guid.Empty))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
user = _userManager.GetUserById(query.UserId);
|
user = _userManager.GetUserById(query.UserId);
|
||||||
}
|
}
|
||||||
|
@ -48,19 +42,19 @@ namespace Emby.Server.Implementations.Library
|
||||||
|
|
||||||
if (query.StartIndex.HasValue)
|
if (query.StartIndex.HasValue)
|
||||||
{
|
{
|
||||||
results = results.Skip(query.StartIndex.Value).ToList();
|
results = results.GetRange(query.StartIndex.Value, totalRecordCount - query.StartIndex.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query.Limit.HasValue)
|
if (query.Limit.HasValue)
|
||||||
{
|
{
|
||||||
results = results.Take(query.Limit.Value).ToList();
|
results = results.GetRange(0, query.Limit.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new QueryResult<SearchHintInfo>
|
return new QueryResult<SearchHintInfo>
|
||||||
{
|
{
|
||||||
TotalRecordCount = totalRecordCount,
|
TotalRecordCount = totalRecordCount,
|
||||||
|
|
||||||
Items = results.ToArray()
|
Items = results
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +79,7 @@ namespace Emby.Server.Implementations.Library
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(searchTerm))
|
if (string.IsNullOrEmpty(searchTerm))
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("SearchTerm can't be empty.", nameof(searchTerm));
|
throw new ArgumentException("SearchTerm can't be empty.", nameof(query));
|
||||||
}
|
}
|
||||||
|
|
||||||
searchTerm = searchTerm.Trim().RemoveDiacritics();
|
searchTerm = searchTerm.Trim().RemoveDiacritics();
|
||||||
|
|
|
@ -199,7 +199,7 @@ namespace MediaBrowser.Controller.Library
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the item.
|
/// Updates the item.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void UpdateItems(IEnumerable<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken);
|
void UpdateItems(IReadOnlyList<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken);
|
||||||
|
|
||||||
void UpdateItem(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken);
|
void UpdateItem(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user