mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-04-22 01:54:42 +01:00
@@ -33,7 +33,8 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
|
||||
// Synology
|
||||
"@eaDir",
|
||||
"eaDir"
|
||||
"eaDir",
|
||||
"#recycle"
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ using MediaBrowser.Server.Implementations.Library.Resolvers;
|
||||
using MoreLinq;
|
||||
using SortOrder = MediaBrowser.Model.Entities.SortOrder;
|
||||
using VideoResolver = MediaBrowser.Naming.Video.VideoResolver;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
|
||||
namespace MediaBrowser.Server.Implementations.Library
|
||||
{
|
||||
@@ -334,15 +335,6 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the item in library cache.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
private void UpdateItemInLibraryCache(BaseItem item)
|
||||
{
|
||||
RegisterItem(item);
|
||||
}
|
||||
|
||||
public void RegisterItem(BaseItem item)
|
||||
{
|
||||
if (item == null)
|
||||
@@ -695,7 +687,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
|
||||
public IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files,
|
||||
IDirectoryService directoryService,
|
||||
Folder parent,
|
||||
Folder parent,
|
||||
LibraryOptions libraryOptions,
|
||||
string collectionType,
|
||||
IItemResolver[] resolvers)
|
||||
@@ -1216,12 +1208,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
if (libraryFolder != null)
|
||||
{
|
||||
info.ItemId = libraryFolder.Id.ToString("N");
|
||||
}
|
||||
|
||||
var collectionFolder = libraryFolder as CollectionFolder;
|
||||
if (collectionFolder != null)
|
||||
{
|
||||
info.LibraryOptions = collectionFolder.GetLibraryOptions();
|
||||
info.LibraryOptions = GetLibraryOptions(libraryFolder);
|
||||
}
|
||||
|
||||
return info;
|
||||
@@ -1490,10 +1477,10 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
|
||||
private void AddUserToQuery(InternalItemsQuery query, User user)
|
||||
{
|
||||
if (query.AncestorIds.Length == 0 &&
|
||||
!query.ParentId.HasValue &&
|
||||
query.ChannelIds.Length == 0 &&
|
||||
query.TopParentIds.Length == 0 &&
|
||||
if (query.AncestorIds.Length == 0 &&
|
||||
!query.ParentId.HasValue &&
|
||||
query.ChannelIds.Length == 0 &&
|
||||
query.TopParentIds.Length == 0 &&
|
||||
string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey)
|
||||
&& query.ItemIds.Length == 0)
|
||||
{
|
||||
@@ -1781,7 +1768,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
|
||||
foreach (var item in list)
|
||||
{
|
||||
UpdateItemInLibraryCache(item);
|
||||
RegisterItem(item);
|
||||
}
|
||||
|
||||
if (ItemAdded != null)
|
||||
@@ -1822,7 +1809,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
|
||||
await ItemRepository.SaveItem(item, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
UpdateItemInLibraryCache(item);
|
||||
RegisterItem(item);
|
||||
|
||||
if (ItemUpdated != null)
|
||||
{
|
||||
@@ -1889,11 +1876,41 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
|
||||
public LibraryOptions GetLibraryOptions(BaseItem item)
|
||||
{
|
||||
var collectionFolder = GetCollectionFolders(item)
|
||||
.OfType<CollectionFolder>()
|
||||
.FirstOrDefault();
|
||||
var collectionFolder = item as CollectionFolder;
|
||||
if (collectionFolder == null)
|
||||
{
|
||||
collectionFolder = GetCollectionFolders(item)
|
||||
.OfType<CollectionFolder>()
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
return collectionFolder == null ? new LibraryOptions() : collectionFolder.GetLibraryOptions();
|
||||
var options = collectionFolder == null ? new LibraryOptions() : collectionFolder.GetLibraryOptions();
|
||||
|
||||
if (options.SchemaVersion < 3)
|
||||
{
|
||||
options.SaveLocalMetadata = ConfigurationManager.Configuration.SaveLocalMeta;
|
||||
options.EnableInternetProviders = ConfigurationManager.Configuration.EnableInternetProviders;
|
||||
}
|
||||
|
||||
if (options.SchemaVersion < 2)
|
||||
{
|
||||
var chapterOptions = ConfigurationManager.GetConfiguration<ChapterOptions>("chapters");
|
||||
options.ExtractChapterImagesDuringLibraryScan = chapterOptions.ExtractDuringLibraryScan;
|
||||
|
||||
if (collectionFolder != null)
|
||||
{
|
||||
if (string.Equals(collectionFolder.CollectionType, "movies", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
options.EnableChapterImageExtraction = chapterOptions.EnableMovieChapterImageExtraction;
|
||||
}
|
||||
else if (string.Equals(collectionFolder.CollectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
options.EnableChapterImageExtraction = chapterOptions.EnableEpisodeChapterImageExtraction;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
public string GetContentType(BaseItem item)
|
||||
@@ -2437,7 +2454,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
|
||||
public IEnumerable<Video> FindTrailers(BaseItem owner, List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
|
||||
{
|
||||
var files = owner.IsInMixedFolder ? new List<FileSystemMetadata>() : fileSystemChildren.Where(i => i.IsDirectory)
|
||||
var files = owner.DetectIsInMixedFolder() ? new List<FileSystemMetadata>() : fileSystemChildren.Where(i => i.IsDirectory)
|
||||
.Where(i => string.Equals(i.Name, BaseItem.TrailerFolderName, StringComparison.OrdinalIgnoreCase))
|
||||
.SelectMany(i => _fileSystem.GetFiles(i.FullName, false))
|
||||
.ToList();
|
||||
@@ -2527,7 +2544,59 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
}).OrderBy(i => i.Path).ToList();
|
||||
}
|
||||
|
||||
public string GetPathAfterNetworkSubstitution(string path, BaseItem ownerItem)
|
||||
{
|
||||
if (ownerItem != null)
|
||||
{
|
||||
var libraryOptions = GetLibraryOptions(ownerItem);
|
||||
if (libraryOptions != null)
|
||||
{
|
||||
foreach (var pathInfo in libraryOptions.PathInfos)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(pathInfo.NetworkPath))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var substitutionResult = SubstitutePathInternal(path, pathInfo.Path, pathInfo.NetworkPath);
|
||||
if (substitutionResult.Item2)
|
||||
{
|
||||
return substitutionResult.Item1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var metadataPath = ConfigurationManager.Configuration.MetadataPath;
|
||||
var metadataNetworkPath = ConfigurationManager.Configuration.MetadataNetworkPath;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(metadataPath) && !string.IsNullOrWhiteSpace(metadataNetworkPath))
|
||||
{
|
||||
var metadataSubstitutionResult = SubstitutePathInternal(path, metadataPath, metadataNetworkPath);
|
||||
if (metadataSubstitutionResult.Item2)
|
||||
{
|
||||
return metadataSubstitutionResult.Item1;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var map in ConfigurationManager.Configuration.PathSubstitutions)
|
||||
{
|
||||
var substitutionResult = SubstitutePathInternal(path, map.From, map.To);
|
||||
if (substitutionResult.Item2)
|
||||
{
|
||||
return substitutionResult.Item1;
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public string SubstitutePath(string path, string from, string to)
|
||||
{
|
||||
return SubstitutePathInternal(path, from, to).Item1;
|
||||
}
|
||||
|
||||
private Tuple<string, bool> SubstitutePathInternal(string path, string from, string to)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
@@ -2542,7 +2611,11 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
throw new ArgumentNullException("to");
|
||||
}
|
||||
|
||||
from = from.Trim();
|
||||
to = to.Trim();
|
||||
|
||||
var newPath = path.Replace(from, to, StringComparison.OrdinalIgnoreCase);
|
||||
var changed = false;
|
||||
|
||||
if (!string.Equals(newPath, path))
|
||||
{
|
||||
@@ -2554,9 +2627,11 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
{
|
||||
newPath = newPath.Replace('/', '\\');
|
||||
}
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
return newPath;
|
||||
return new Tuple<string, bool>(newPath, changed);
|
||||
}
|
||||
|
||||
private void SetExtraTypeFromFilename(Video item)
|
||||
@@ -2685,7 +2760,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
public void AddVirtualFolder(string name, string collectionType, string[] mediaPaths, LibraryOptions options, bool refreshLibrary)
|
||||
public void AddVirtualFolder(string name, string collectionType, LibraryOptions options, bool refreshLibrary)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
@@ -2703,12 +2778,13 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
virtualFolderPath = Path.Combine(rootFolderPath, name);
|
||||
}
|
||||
|
||||
if (mediaPaths != null)
|
||||
var mediaPathInfos = options.PathInfos;
|
||||
if (mediaPathInfos != null)
|
||||
{
|
||||
var invalidpath = mediaPaths.FirstOrDefault(i => !_fileSystem.DirectoryExists(i));
|
||||
var invalidpath = mediaPathInfos.FirstOrDefault(i => !_fileSystem.DirectoryExists(i.Path));
|
||||
if (invalidpath != null)
|
||||
{
|
||||
throw new ArgumentException("The specified path does not exist: " + invalidpath + ".");
|
||||
throw new ArgumentException("The specified path does not exist: " + invalidpath.Path + ".");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2730,11 +2806,11 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
|
||||
CollectionFolder.SaveLibraryOptions(virtualFolderPath, options);
|
||||
|
||||
if (mediaPaths != null)
|
||||
if (mediaPathInfos != null)
|
||||
{
|
||||
foreach (var path in mediaPaths)
|
||||
foreach (var path in mediaPathInfos)
|
||||
{
|
||||
AddMediaPath(name, path);
|
||||
AddMediaPathInternal(name, path, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2760,6 +2836,137 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
}
|
||||
}
|
||||
|
||||
private bool ValidateNetworkPath(string path)
|
||||
{
|
||||
if (Environment.OSVersion.Platform == PlatformID.Win32NT || !path.StartsWith("\\\\", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return Directory.Exists(path);
|
||||
}
|
||||
|
||||
// Without native support for unc, we cannot validate this when running under mono
|
||||
return true;
|
||||
}
|
||||
|
||||
private const string ShortcutFileExtension = ".mblink";
|
||||
private const string ShortcutFileSearch = "*" + ShortcutFileExtension;
|
||||
public void AddMediaPath(string virtualFolderName, MediaPathInfo pathInfo)
|
||||
{
|
||||
AddMediaPathInternal(virtualFolderName, pathInfo, true);
|
||||
}
|
||||
|
||||
private void AddMediaPathInternal(string virtualFolderName, MediaPathInfo pathInfo, bool saveLibraryOptions)
|
||||
{
|
||||
if (pathInfo == null)
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
var path = pathInfo.Path;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
if (!_fileSystem.DirectoryExists(path))
|
||||
{
|
||||
throw new DirectoryNotFoundException("The path does not exist.");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(pathInfo.NetworkPath) && !ValidateNetworkPath(pathInfo.NetworkPath))
|
||||
{
|
||||
throw new DirectoryNotFoundException("The network path does not exist.");
|
||||
}
|
||||
|
||||
var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
|
||||
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
|
||||
|
||||
var shortcutFilename = _fileSystem.GetFileNameWithoutExtension(path);
|
||||
|
||||
var lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension);
|
||||
|
||||
while (_fileSystem.FileExists(lnk))
|
||||
{
|
||||
shortcutFilename += "1";
|
||||
lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension);
|
||||
}
|
||||
|
||||
_fileSystem.CreateShortcut(lnk, path);
|
||||
|
||||
RemoveContentTypeOverrides(path);
|
||||
|
||||
if (saveLibraryOptions)
|
||||
{
|
||||
var libraryOptions = CollectionFolder.GetLibraryOptions(virtualFolderPath);
|
||||
|
||||
var list = libraryOptions.PathInfos.ToList();
|
||||
list.Add(pathInfo);
|
||||
libraryOptions.PathInfos = list.ToArray();
|
||||
|
||||
SyncLibraryOptionsToLocations(virtualFolderPath, libraryOptions);
|
||||
|
||||
CollectionFolder.SaveLibraryOptions(virtualFolderPath, libraryOptions);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateMediaPath(string virtualFolderName, MediaPathInfo pathInfo)
|
||||
{
|
||||
if (pathInfo == null)
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(pathInfo.NetworkPath) && !ValidateNetworkPath(pathInfo.NetworkPath))
|
||||
{
|
||||
throw new DirectoryNotFoundException("The network path does not exist.");
|
||||
}
|
||||
|
||||
var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
|
||||
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
|
||||
|
||||
var libraryOptions = CollectionFolder.GetLibraryOptions(virtualFolderPath);
|
||||
|
||||
SyncLibraryOptionsToLocations(virtualFolderPath, libraryOptions);
|
||||
|
||||
var list = libraryOptions.PathInfos.ToList();
|
||||
foreach (var originalPathInfo in list)
|
||||
{
|
||||
if (string.Equals(pathInfo.Path, originalPathInfo.Path, StringComparison.Ordinal))
|
||||
{
|
||||
originalPathInfo.NetworkPath = pathInfo.NetworkPath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
libraryOptions.PathInfos = list.ToArray();
|
||||
|
||||
CollectionFolder.SaveLibraryOptions(virtualFolderPath, libraryOptions);
|
||||
}
|
||||
|
||||
private void SyncLibraryOptionsToLocations(string virtualFolderPath, LibraryOptions options)
|
||||
{
|
||||
var topLibraryFolders = GetUserRootFolder().Children.ToList();
|
||||
var info = GetVirtualFolderInfo(virtualFolderPath, topLibraryFolders);
|
||||
|
||||
if (info.Locations.Count > 0 && info.Locations.Count != options.PathInfos.Length)
|
||||
{
|
||||
var list = options.PathInfos.ToList();
|
||||
|
||||
foreach (var location in info.Locations)
|
||||
{
|
||||
if (!list.Any(i => string.Equals(i.Path, location, StringComparison.Ordinal)))
|
||||
{
|
||||
list.Add(new MediaPathInfo
|
||||
{
|
||||
Path = location
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
options.PathInfos = list.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveVirtualFolder(string name, bool refreshLibrary)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
@@ -2804,38 +3011,6 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
}
|
||||
}
|
||||
|
||||
private const string ShortcutFileExtension = ".mblink";
|
||||
private const string ShortcutFileSearch = "*" + ShortcutFileExtension;
|
||||
public void AddMediaPath(string virtualFolderName, string path)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
if (!_fileSystem.DirectoryExists(path))
|
||||
{
|
||||
throw new DirectoryNotFoundException("The path does not exist.");
|
||||
}
|
||||
|
||||
var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
|
||||
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
|
||||
|
||||
var shortcutFilename = _fileSystem.GetFileNameWithoutExtension(path);
|
||||
|
||||
var lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension);
|
||||
|
||||
while (_fileSystem.FileExists(lnk))
|
||||
{
|
||||
shortcutFilename += "1";
|
||||
lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension);
|
||||
}
|
||||
|
||||
_fileSystem.CreateShortcut(lnk, path);
|
||||
|
||||
RemoveContentTypeOverrides(path);
|
||||
}
|
||||
|
||||
private void RemoveContentTypeOverrides(string path)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
@@ -2872,19 +3047,28 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
}
|
||||
|
||||
var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
|
||||
var path = Path.Combine(rootFolderPath, virtualFolderName);
|
||||
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
|
||||
|
||||
if (!_fileSystem.DirectoryExists(path))
|
||||
if (!_fileSystem.DirectoryExists(virtualFolderPath))
|
||||
{
|
||||
throw new DirectoryNotFoundException(string.Format("The media collection {0} does not exist", virtualFolderName));
|
||||
}
|
||||
|
||||
var shortcut = Directory.EnumerateFiles(path, ShortcutFileSearch, SearchOption.AllDirectories).FirstOrDefault(f => _fileSystem.ResolveShortcut(f).Equals(mediaPath, StringComparison.OrdinalIgnoreCase));
|
||||
var shortcut = Directory.EnumerateFiles(virtualFolderPath, ShortcutFileSearch, SearchOption.AllDirectories).FirstOrDefault(f => _fileSystem.ResolveShortcut(f).Equals(mediaPath, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (!string.IsNullOrEmpty(shortcut))
|
||||
{
|
||||
_fileSystem.DeleteFile(shortcut);
|
||||
}
|
||||
|
||||
var libraryOptions = CollectionFolder.GetLibraryOptions(virtualFolderPath);
|
||||
|
||||
libraryOptions.PathInfos = libraryOptions
|
||||
.PathInfos
|
||||
.Where(i => !string.Equals(i.Path, mediaPath, StringComparison.Ordinal))
|
||||
.ToArray();
|
||||
|
||||
CollectionFolder.SaveLibraryOptions(virtualFolderPath, libraryOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -221,8 +221,28 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<MediaSourceInfo> GetMediaSource(IHasMediaSources item, string mediaSourceId, bool enablePathSubstitution)
|
||||
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);
|
||||
|
||||
@@ -335,7 +355,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private readonly ConcurrentDictionary<string, LiveStreamInfo> _openStreams = new ConcurrentDictionary<string, LiveStreamInfo>(StringComparer.OrdinalIgnoreCase);
|
||||
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)
|
||||
@@ -347,7 +367,9 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
var tuple = GetProvider(request.OpenToken);
|
||||
var provider = tuple.Item1;
|
||||
|
||||
var mediaSource = await provider.OpenMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false);
|
||||
var mediaSourceTuple = await provider.OpenMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var mediaSource = mediaSourceTuple.Item1;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(mediaSource.LiveStreamId))
|
||||
{
|
||||
@@ -361,9 +383,11 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
Date = DateTime.UtcNow,
|
||||
EnableCloseTimer = enableAutoClose,
|
||||
Id = mediaSource.LiveStreamId,
|
||||
MediaSource = mediaSource
|
||||
MediaSource = mediaSource,
|
||||
DirectStreamProvider = mediaSourceTuple.Item2
|
||||
};
|
||||
_openStreams.AddOrUpdate(mediaSource.LiveStreamId, info, (key, i) => info);
|
||||
|
||||
_openStreams[mediaSource.LiveStreamId] = info;
|
||||
|
||||
if (enableAutoClose)
|
||||
{
|
||||
@@ -394,14 +418,14 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<MediaSourceInfo> GetLiveStream(string id, CancellationToken cancellationToken)
|
||||
public async Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetLiveStreamWithDirectStreamProvider(string id, CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(id))
|
||||
{
|
||||
throw new ArgumentNullException("id");
|
||||
}
|
||||
|
||||
_logger.Debug("Getting live stream {0}", id);
|
||||
_logger.Debug("Getting already opened live stream {0}", id);
|
||||
|
||||
await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
@@ -410,7 +434,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
LiveStreamInfo info;
|
||||
if (_openStreams.TryGetValue(id, out info))
|
||||
{
|
||||
return info.MediaSource;
|
||||
return new Tuple<MediaSourceInfo, IDirectStreamProvider>(info.MediaSource, info.DirectStreamProvider);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -423,6 +447,12 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -436,7 +466,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Error("Failed to update MediaSource timestamp for {0}", id);
|
||||
_logger.Error("Failed to ping live stream {0}", id);
|
||||
}
|
||||
}
|
||||
finally
|
||||
@@ -445,17 +475,16 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CloseLiveStreamWithProvider(IMediaSourceProvider provider, string streamId, CancellationToken cancellationToken)
|
||||
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, cancellationToken).ConfigureAwait(false);
|
||||
await provider.CloseMediaSource(streamId).ConfigureAwait(false);
|
||||
}
|
||||
catch (NotImplementedException)
|
||||
{
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -463,37 +492,35 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CloseLiveStream(string id, CancellationToken cancellationToken)
|
||||
public async Task CloseLiveStream(string id)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(id))
|
||||
{
|
||||
throw new ArgumentNullException("id");
|
||||
}
|
||||
|
||||
await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
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, cancellationToken).ConfigureAwait(false);
|
||||
await CloseLiveStreamWithProvider(tuple.Item1, tuple.Item2).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
LiveStreamInfo removed;
|
||||
if (_openStreams.TryRemove(id, out removed))
|
||||
{
|
||||
removed.Closed = true;
|
||||
}
|
||||
|
||||
if (_openStreams.Count == 0)
|
||||
{
|
||||
StopCloseTimer();
|
||||
if (_openStreams.Count == 0)
|
||||
{
|
||||
StopCloseTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
@@ -523,7 +550,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
}
|
||||
|
||||
private Timer _closeTimer;
|
||||
private readonly TimeSpan _openStreamMaxAge = TimeSpan.FromSeconds(60);
|
||||
private readonly TimeSpan _openStreamMaxAge = TimeSpan.FromSeconds(180);
|
||||
|
||||
private void StartCloseTimer()
|
||||
{
|
||||
@@ -545,10 +572,20 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
|
||||
private async void CloseTimerCallback(object state)
|
||||
{
|
||||
var infos = _openStreams
|
||||
.Values
|
||||
.Where(i => i.EnableCloseTimer && DateTime.UtcNow - i.Date > _openStreamMaxAge)
|
||||
.ToList();
|
||||
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)
|
||||
{
|
||||
@@ -556,7 +593,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
{
|
||||
try
|
||||
{
|
||||
await CloseLiveStream(info.Id, CancellationToken.None).ConfigureAwait(false);
|
||||
await CloseLiveStream(info.Id).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -588,12 +625,10 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
{
|
||||
foreach (var key in _openStreams.Keys.ToList())
|
||||
{
|
||||
var task = CloseLiveStream(key, CancellationToken.None);
|
||||
var task = CloseLiveStream(key);
|
||||
|
||||
Task.WaitAll(task);
|
||||
}
|
||||
|
||||
_openStreams.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -605,6 +640,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
public string Id;
|
||||
public bool Closed;
|
||||
public MediaSourceInfo MediaSource;
|
||||
public IDirectStreamProvider DirectStreamProvider;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,8 +54,8 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
|
||||
if (!args.IsDirectory) return null;
|
||||
|
||||
// Avoid mis-identifying top folders
|
||||
if (args.Parent.IsRoot) return null;
|
||||
if (args.HasParent<MusicAlbum>()) return null;
|
||||
if (args.Parent.IsRoot) return null;
|
||||
|
||||
var collectionType = args.GetCollectionType();
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using CommonIO;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
|
||||
namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
|
||||
{
|
||||
@@ -18,12 +19,14 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
|
||||
private readonly ILogger _logger;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly IServerConfigurationManager _config;
|
||||
|
||||
public MusicArtistResolver(ILogger logger, IFileSystem fileSystem, ILibraryManager libraryManager)
|
||||
public MusicArtistResolver(ILogger logger, IFileSystem fileSystem, ILibraryManager libraryManager, IServerConfigurationManager config)
|
||||
{
|
||||
_logger = logger;
|
||||
_fileSystem = fileSystem;
|
||||
_libraryManager = libraryManager;
|
||||
_config = config;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -48,9 +51,6 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
|
||||
{
|
||||
if (!args.IsDirectory) return null;
|
||||
|
||||
// Avoid mis-identifying top folders
|
||||
if (args.Parent.IsRoot) return null;
|
||||
|
||||
// Don't allow nested artists
|
||||
if (args.HasParent<MusicArtist>() || args.HasParent<MusicAlbum>())
|
||||
{
|
||||
@@ -67,6 +67,19 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
|
||||
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);
|
||||
|
||||
@@ -51,7 +51,6 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
|
||||
base.SetInitialItemValues(item, args);
|
||||
|
||||
item.IsRoot = args.Parent == null;
|
||||
item.IsPhysicalRoot = args.IsPhysicalRoot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,12 +48,6 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
|
||||
string collectionType,
|
||||
IDirectoryService directoryService)
|
||||
{
|
||||
if (parent != null && parent.Path != null && parent.Path.IndexOf("disney", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
var b = true;
|
||||
var a = b;
|
||||
}
|
||||
|
||||
var result = ResolveMultipleInternal(parent, files, collectionType, directoryService);
|
||||
|
||||
if (result != null)
|
||||
@@ -213,26 +207,22 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
|
||||
// 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);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return FindMovie<Video>(args.Path, args.Parent, files, args.DirectoryService, collectionType);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(collectionType))
|
||||
{
|
||||
// Owned items should just use the plain video type
|
||||
// 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>())
|
||||
@@ -240,11 +230,21 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
|
||||
return null;
|
||||
}
|
||||
|
||||
return FindMovie<Movie>(args.Path, args.Parent, files, args.DirectoryService, collectionType);
|
||||
{
|
||||
var files = args.FileSystemChildren
|
||||
.Where(i => !LibraryManager.IgnoreFile(i, args.Parent))
|
||||
.ToList();
|
||||
|
||||
return FindMovie<Movie>(args.Path, args.Parent, files, args.DirectoryService, collectionType);
|
||||
}
|
||||
}
|
||||
|
||||
if (string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var files = args.FileSystemChildren
|
||||
.Where(i => !LibraryManager.IgnoreFile(i, args.Parent))
|
||||
.ToList();
|
||||
|
||||
return FindMovie<Movie>(args.Path, args.Parent, files, args.DirectoryService, collectionType);
|
||||
}
|
||||
|
||||
|
||||
@@ -54,11 +54,20 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
|
||||
{
|
||||
if (args.IsDirectory)
|
||||
{
|
||||
if (args.HasParent<Series>())
|
||||
if (args.HasParent<Series>() || args.HasParent<Season>())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (args.ContainsFileSystemEntryByName("tvshow.nfo"))
|
||||
{
|
||||
return new Series
|
||||
{
|
||||
Path = args.Path,
|
||||
Name = Path.GetFileName(args.Path)
|
||||
};
|
||||
}
|
||||
|
||||
var collectionType = args.GetCollectionType();
|
||||
if (string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
@@ -72,22 +81,20 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (string.IsNullOrWhiteSpace(collectionType))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(collectionType))
|
||||
if (args.Parent.IsRoot)
|
||||
{
|
||||
if (args.Parent.IsRoot)
|
||||
return null;
|
||||
}
|
||||
|
||||
if (IsSeriesFolder(args.Path, args.FileSystemChildren, args.DirectoryService, _fileSystem, _logger, _libraryManager, args.GetLibraryOptions(), false))
|
||||
{
|
||||
return new Series
|
||||
{
|
||||
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)
|
||||
};
|
||||
}
|
||||
Path = args.Path,
|
||||
Name = Path.GetFileName(args.Path)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,6 +105,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
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)))
|
||||
{
|
||||
|
||||
@@ -61,16 +61,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
|
||||
foreach (var key in keys)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Repository.SaveUserData(userId, key, userData, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error saving user data", ex);
|
||||
|
||||
throw;
|
||||
}
|
||||
await Repository.SaveUserData(userId, key, userData, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var cacheKey = GetCacheKey(userId, item.Id);
|
||||
@@ -107,18 +98,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
try
|
||||
{
|
||||
await Repository.SaveAllUserData(userId, userData, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error saving user data", ex);
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
await Repository.SaveAllUserData(userId, userData, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -289,6 +269,11 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
positionTicks = 0;
|
||||
}
|
||||
|
||||
if (!item.SupportsPlayedStatus)
|
||||
{
|
||||
positionTicks = 0;
|
||||
data.Played = false;
|
||||
}
|
||||
if (item is Audio)
|
||||
{
|
||||
positionTicks = 0;
|
||||
|
||||
Reference in New Issue
Block a user