aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/Library
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations/Library')
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs40
-rw-r--r--Emby.Server.Implementations/Library/MediaSourceManager.cs10
-rw-r--r--Emby.Server.Implementations/Library/PathManager.cs45
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs4
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs4
-rw-r--r--Emby.Server.Implementations/Library/SplashscreenPostScanTask.cs13
-rw-r--r--Emby.Server.Implementations/Library/UserDataManager.cs14
-rw-r--r--Emby.Server.Implementations/Library/Validators/GenresValidator.cs23
8 files changed, 119 insertions, 34 deletions
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index c6b8501cb..40045782b 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -2,7 +2,6 @@
#pragma warning disable CA5394
using System;
-using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
@@ -11,6 +10,7 @@ using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
+using BitFaster.Caching.Lru;
using Emby.Naming.Common;
using Emby.Naming.TV;
using Emby.Server.Implementations.Library.Resolvers;
@@ -64,7 +64,6 @@ namespace Emby.Server.Implementations.Library
private const string ShortcutFileExtension = ".mblink";
private readonly ILogger<LibraryManager> _logger;
- private readonly ConcurrentDictionary<Guid, BaseItem> _cache;
private readonly ITaskManager _taskManager;
private readonly IUserManager _userManager;
private readonly IUserDataManager _userDataRepository;
@@ -81,6 +80,7 @@ namespace Emby.Server.Implementations.Library
private readonly IPeopleRepository _peopleRepository;
private readonly ExtraResolver _extraResolver;
private readonly IPathManager _pathManager;
+ private readonly FastConcurrentLru<Guid, BaseItem> _cache;
/// <summary>
/// The _root folder sync lock.
@@ -150,7 +150,9 @@ namespace Emby.Server.Implementations.Library
_mediaEncoder = mediaEncoder;
_itemRepository = itemRepository;
_imageProcessor = imageProcessor;
- _cache = new ConcurrentDictionary<Guid, BaseItem>();
+
+ _cache = new FastConcurrentLru<Guid, BaseItem>(_configurationManager.Configuration.CacheSize);
+
_namingOptions = namingOptions;
_peopleRepository = peopleRepository;
_pathManager = pathManager;
@@ -158,7 +160,7 @@ namespace Emby.Server.Implementations.Library
_configurationManager.ConfigurationUpdated += ConfigurationUpdated;
- RecordConfigurationValues(configurationManager.Configuration);
+ RecordConfigurationValues(_configurationManager.Configuration);
}
/// <summary>
@@ -306,7 +308,7 @@ namespace Emby.Server.Implementations.Library
}
}
- _cache[item.Id] = item;
+ _cache.AddOrUpdate(item.Id, item);
}
public void DeleteItem(BaseItem item, DeleteOptions options)
@@ -460,14 +462,13 @@ namespace Emby.Server.Implementations.Library
item.SetParent(null);
_itemRepository.DeleteItem(item.Id);
+ _cache.TryRemove(item.Id, out _);
foreach (var child in children)
{
_itemRepository.DeleteItem(child.Id);
_cache.TryRemove(child.Id, out _);
}
- _cache.TryRemove(item.Id, out _);
-
ReportItemRemoved(item, parent);
}
@@ -491,7 +492,24 @@ namespace Emby.Server.Implementations.Library
if (item is Video video)
{
+ // Trickplay
list.Add(_pathManager.GetTrickplayDirectory(video));
+
+ // Subtitles and attachments
+ foreach (var mediaSource in item.GetMediaSources(false))
+ {
+ var subtitleFolder = _pathManager.GetSubtitleFolderPath(mediaSource.Id);
+ if (subtitleFolder is not null)
+ {
+ list.Add(subtitleFolder);
+ }
+
+ var attachmentFolder = _pathManager.GetAttachmentFolderPath(mediaSource.Id);
+ if (attachmentFolder is not null)
+ {
+ list.Add(attachmentFolder);
+ }
+ }
}
return list;
@@ -1255,7 +1273,7 @@ namespace Emby.Server.Implementations.Library
throw new ArgumentException("Guid can't be empty", nameof(id));
}
- if (_cache.TryGetValue(id, out BaseItem? item))
+ if (_cache.TryGet(id, out var item))
{
return item;
}
@@ -1272,7 +1290,7 @@ namespace Emby.Server.Implementations.Library
/// <inheritdoc />
public T? GetItemById<T>(Guid id)
- where T : BaseItem
+ where T : BaseItem
{
var item = GetItemById(id);
if (item is T typedItem)
@@ -2708,7 +2726,7 @@ namespace Emby.Server.Implementations.Library
public IEnumerable<BaseItem> FindExtras(BaseItem owner, IReadOnlyList<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
{
- var ownerVideoInfo = VideoResolver.Resolve(owner.Path, owner.IsFolder, _namingOptions);
+ var ownerVideoInfo = VideoResolver.Resolve(owner.Path, owner.IsFolder, _namingOptions, libraryRoot: owner.ContainingFolderPath);
if (ownerVideoInfo is null)
{
yield break;
@@ -2903,7 +2921,7 @@ namespace Emby.Server.Implementations.Library
throw new ArgumentNullException(nameof(name));
}
- name = _fileSystem.GetValidFilename(name);
+ name = _fileSystem.GetValidFilename(name.Trim());
var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs
index afe5b14e9..c6cfd5391 100644
--- a/Emby.Server.Implementations/Library/MediaSourceManager.cs
+++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs
@@ -427,6 +427,7 @@ namespace Emby.Server.Implementations.Library
if (source.MediaStreams.Any(i => i.Type == MediaStreamType.Audio && i.Index == index))
{
source.DefaultAudioStreamIndex = index;
+ source.DefaultAudioIndexSource = AudioIndexSource.User;
return;
}
}
@@ -434,6 +435,15 @@ namespace Emby.Server.Implementations.Library
var preferredAudio = NormalizeLanguage(user.AudioLanguagePreference);
source.DefaultAudioStreamIndex = MediaStreamSelector.GetDefaultAudioStreamIndex(source.MediaStreams, preferredAudio, user.PlayDefaultAudioTrack);
+ if (user.PlayDefaultAudioTrack)
+ {
+ source.DefaultAudioIndexSource |= AudioIndexSource.Default;
+ }
+
+ if (preferredAudio.Count > 0)
+ {
+ source.DefaultAudioIndexSource |= AudioIndexSource.Language;
+ }
}
public void SetDefaultAudioAndSubtitleStreamIndices(BaseItem item, MediaSourceInfo source, User user)
diff --git a/Emby.Server.Implementations/Library/PathManager.cs b/Emby.Server.Implementations/Library/PathManager.cs
index c910abadb..83a6df964 100644
--- a/Emby.Server.Implementations/Library/PathManager.cs
+++ b/Emby.Server.Implementations/Library/PathManager.cs
@@ -1,5 +1,7 @@
+using System;
using System.Globalization;
using System.IO;
+using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.IO;
@@ -12,25 +14,60 @@ namespace Emby.Server.Implementations.Library;
public class PathManager : IPathManager
{
private readonly IServerConfigurationManager _config;
+ private readonly IApplicationPaths _appPaths;
/// <summary>
/// Initializes a new instance of the <see cref="PathManager"/> class.
/// </summary>
/// <param name="config">The server configuration manager.</param>
+ /// <param name="appPaths">The application paths.</param>
public PathManager(
- IServerConfigurationManager config)
+ IServerConfigurationManager config,
+ IApplicationPaths appPaths)
{
_config = config;
+ _appPaths = appPaths;
+ }
+
+ private string SubtitleCachePath => Path.Combine(_appPaths.DataPath, "subtitles");
+
+ private string AttachmentCachePath => Path.Combine(_appPaths.DataPath, "attachments");
+
+ /// <inheritdoc />
+ public string GetAttachmentPath(string mediaSourceId, string fileName)
+ {
+ return Path.Join(GetAttachmentFolderPath(mediaSourceId), fileName);
+ }
+
+ /// <inheritdoc />
+ public string GetAttachmentFolderPath(string mediaSourceId)
+ {
+ var id = Guid.Parse(mediaSourceId).ToString("D", CultureInfo.InvariantCulture).AsSpan();
+
+ return Path.Join(AttachmentCachePath, id[..2], id);
+ }
+
+ /// <inheritdoc />
+ public string GetSubtitleFolderPath(string mediaSourceId)
+ {
+ var id = Guid.Parse(mediaSourceId).ToString("D", CultureInfo.InvariantCulture).AsSpan();
+
+ return Path.Join(SubtitleCachePath, id[..2], id);
+ }
+
+ /// <inheritdoc />
+ public string GetSubtitlePath(string mediaSourceId, int streamIndex, string extension)
+ {
+ return Path.Join(GetSubtitleFolderPath(mediaSourceId), streamIndex.ToString(CultureInfo.InvariantCulture) + extension);
}
/// <inheritdoc />
public string GetTrickplayDirectory(BaseItem item, bool saveWithMedia = false)
{
- var basePath = _config.ApplicationPaths.TrickplayPath;
- var idString = item.Id.ToString("N", CultureInfo.InvariantCulture);
+ var id = item.Id.ToString("D", CultureInfo.InvariantCulture).AsSpan();
return saveWithMedia
? Path.Combine(item.ContainingFolderPath, Path.ChangeExtension(item.Path, ".trickplay"))
- : Path.Combine(basePath, idString);
+ : Path.Join(_config.ApplicationPaths.TrickplayPath, id[..2], id);
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs b/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs
index b4791b945..b9f9f2972 100644
--- a/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs
@@ -54,9 +54,9 @@ namespace Emby.Server.Implementations.Library.Resolvers
_ => _videoResolvers
};
- public bool TryGetExtraTypeForOwner(string path, VideoFileInfo ownerVideoFileInfo, [NotNullWhen(true)] out ExtraType? extraType)
+ public bool TryGetExtraTypeForOwner(string path, VideoFileInfo ownerVideoFileInfo, [NotNullWhen(true)] out ExtraType? extraType, string? libraryRoot = "")
{
- var extraResult = GetExtraInfo(path, _namingOptions);
+ var extraResult = GetExtraInfo(path, _namingOptions, libraryRoot);
if (extraResult.ExtraType is null)
{
extraType = null;
diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
index 4debe722b..f1aeb1340 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
@@ -270,11 +270,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
}
var videoInfos = files
- .Select(i => VideoResolver.Resolve(i.FullName, i.IsDirectory, NamingOptions, parseName))
+ .Select(i => VideoResolver.Resolve(i.FullName, i.IsDirectory, NamingOptions, parseName, parent.ContainingFolderPath))
.Where(f => f is not null)
.ToList();
- var resolverResult = VideoListResolver.Resolve(videoInfos, NamingOptions, supportMultiEditions, parseName);
+ var resolverResult = VideoListResolver.Resolve(videoInfos, NamingOptions, supportMultiEditions, parseName, parent.ContainingFolderPath);
var result = new MultiItemResolverResult
{
diff --git a/Emby.Server.Implementations/Library/SplashscreenPostScanTask.cs b/Emby.Server.Implementations/Library/SplashscreenPostScanTask.cs
index 0c9edd839..71ce3b601 100644
--- a/Emby.Server.Implementations/Library/SplashscreenPostScanTask.cs
+++ b/Emby.Server.Implementations/Library/SplashscreenPostScanTask.cs
@@ -11,7 +11,6 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Querying;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library;
@@ -78,15 +77,15 @@ public class SplashscreenPostScanTask : ILibraryPostScanTask
CollapseBoxSetItems = false,
Recursive = true,
DtoOptions = new DtoOptions(false),
- ImageTypes = new[] { imageType },
+ ImageTypes = [imageType],
Limit = 30,
// TODO max parental rating configurable
- MaxParentalRating = 10,
- OrderBy = new[]
- {
+ MaxParentalRating = new(10, null),
+ OrderBy =
+ [
(ItemSortBy.Random, SortOrder.Ascending)
- },
- IncludeItemTypes = new[] { BaseItemKind.Movie, BaseItemKind.Series }
+ ],
+ IncludeItemTypes = [BaseItemKind.Movie, BaseItemKind.Series]
});
}
}
diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs
index 8b88b904b..be1d96bf0 100644
--- a/Emby.Server.Implementations/Library/UserDataManager.cs
+++ b/Emby.Server.Implementations/Library/UserDataManager.cs
@@ -1,14 +1,13 @@
#pragma warning disable RS0030 // Do not use banned APIs
using System;
-using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
+using BitFaster.Caching.Lru;
using Jellyfin.Database.Implementations;
using Jellyfin.Database.Implementations.Entities;
-using Jellyfin.Extensions;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
@@ -26,11 +25,9 @@ namespace Emby.Server.Implementations.Library
/// </summary>
public class UserDataManager : IUserDataManager
{
- private readonly ConcurrentDictionary<string, UserItemData> _userData =
- new ConcurrentDictionary<string, UserItemData>(StringComparer.OrdinalIgnoreCase);
-
private readonly IServerConfigurationManager _config;
private readonly IDbContextFactory<JellyfinDbContext> _repository;
+ private readonly FastConcurrentLru<string, UserItemData> _cache;
/// <summary>
/// Initializes a new instance of the <see cref="UserDataManager"/> class.
@@ -43,6 +40,7 @@ namespace Emby.Server.Implementations.Library
{
_config = config;
_repository = repository;
+ _cache = new FastConcurrentLru<string, UserItemData>(Environment.ProcessorCount, _config.Configuration.CacheSize, StringComparer.OrdinalIgnoreCase);
}
/// <inheritdoc />
@@ -81,7 +79,7 @@ namespace Emby.Server.Implementations.Library
var userId = user.InternalId;
var cacheKey = GetCacheKey(userId, item.Id);
- _userData.AddOrUpdate(cacheKey, userData, (_, _) => userData);
+ _cache.AddOrUpdate(cacheKey, userData);
UserDataSaved?.Invoke(this, new UserDataSaveEventArgs
{
@@ -182,7 +180,7 @@ namespace Emby.Server.Implementations.Library
{
var cacheKey = GetCacheKey(user.InternalId, itemId);
- if (_userData.TryGetValue(cacheKey, out var data))
+ if (_cache.TryGet(cacheKey, out var data))
{
return data;
}
@@ -197,7 +195,7 @@ namespace Emby.Server.Implementations.Library
};
}
- return _userData.GetOrAdd(cacheKey, data);
+ return _cache.GetOrAdd(cacheKey, _ => data);
}
private UserItemData? GetUserDataInternal(Guid userId, Guid itemId, List<string> keys)
diff --git a/Emby.Server.Implementations/Library/Validators/GenresValidator.cs b/Emby.Server.Implementations/Library/Validators/GenresValidator.cs
index e59c62e23..364770fcd 100644
--- a/Emby.Server.Implementations/Library/Validators/GenresValidator.cs
+++ b/Emby.Server.Implementations/Library/Validators/GenresValidator.cs
@@ -1,6 +1,9 @@
using System;
+using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using Microsoft.Extensions.Logging;
@@ -75,6 +78,26 @@ namespace Emby.Server.Implementations.Library.Validators
progress.Report(percent);
}
+ var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery
+ {
+ IncludeItemTypes = [BaseItemKind.Genre, BaseItemKind.MusicGenre],
+ IsDeadGenre = true,
+ IsLocked = false
+ });
+
+ foreach (var item in deadEntities)
+ {
+ _logger.LogInformation("Deleting dead {ItemType} {ItemId} {ItemName}", item.GetType().Name, item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name);
+
+ _libraryManager.DeleteItem(
+ item,
+ new DeleteOptions
+ {
+ DeleteFileLocation = false
+ },
+ false);
+ }
+
progress.Report(100);
}
}