diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 6d87af538c..d5dcb785c1 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -20,18 +20,18 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3
+ uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3
- name: Setup .NET
uses: actions/setup-dotnet@607fce577a46308457984d59e4954e075820f10a # tag=v3
with:
dotnet-version: '7.0.x'
- name: Initialize CodeQL
- uses: github/codeql-action/init@32dc499307d133bb5085bae78498c0ac2cf762d5 # v2
+ uses: github/codeql-action/init@168b99b3c22180941ae7dbdd5f5c9678ede476ba # v2
with:
languages: ${{ matrix.language }}
queries: +security-extended
- name: Autobuild
- uses: github/codeql-action/autobuild@32dc499307d133bb5085bae78498c0ac2cf762d5 # v2
+ uses: github/codeql-action/autobuild@168b99b3c22180941ae7dbdd5f5c9678ede476ba # v2
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@32dc499307d133bb5085bae78498c0ac2cf762d5 # v2
+ uses: github/codeql-action/analyze@168b99b3c22180941ae7dbdd5f5c9678ede476ba # v2
diff --git a/.github/workflows/commands.yml b/.github/workflows/commands.yml
index 75227c57b9..c0b365b02f 100644
--- a/.github/workflows/commands.yml
+++ b/.github/workflows/commands.yml
@@ -24,7 +24,7 @@ jobs:
reactions: '+1'
- name: Checkout the latest code
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3
+ uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3
with:
token: ${{ secrets.JF_BOT_TOKEN }}
fetch-depth: 0
@@ -51,7 +51,7 @@ jobs:
reactions: eyes
- name: Checkout the latest code
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3
+ uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3
with:
token: ${{ secrets.JF_BOT_TOKEN }}
fetch-depth: 0
diff --git a/.github/workflows/openapi.yml b/.github/workflows/openapi.yml
index aa2e0417fe..a67ce90d22 100644
--- a/.github/workflows/openapi.yml
+++ b/.github/workflows/openapi.yml
@@ -14,7 +14,7 @@ jobs:
permissions: read-all
steps:
- name: Checkout repository
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3
+ uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3
with:
ref: ${{ github.event.pull_request.head.sha }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
@@ -39,7 +39,7 @@ jobs:
permissions: read-all
steps:
- name: Checkout repository
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3
+ uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3
with:
ref: ${{ github.event.pull_request.head.sha }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 270ea2f17b..d6e055899d 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -17,30 +17,30 @@
-
+
-
-
+
+
-
+
-
+
-
-
-
-
+
+
+
+
-
+
-
-
+
+
@@ -52,7 +52,7 @@
-
+
diff --git a/Dockerfile b/Dockerfile
index f5f5787bec..e51d285e12 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -10,6 +10,7 @@ RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-
&& curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& cd jellyfin-web-* \
&& npm ci --no-audit --unsafe-perm \
+ && npm run build:production \
&& mv dist /dist
FROM debian:stable-slim as app
diff --git a/Dockerfile.arm b/Dockerfile.arm
index bbb84a461c..46a3e9b998 100644
--- a/Dockerfile.arm
+++ b/Dockerfile.arm
@@ -11,6 +11,7 @@ RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-
&& curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& cd jellyfin-web-* \
&& npm ci --no-audit --unsafe-perm \
+ && npm run build:production \
&& mv dist /dist
FROM multiarch/qemu-user-static:x86_64-arm as qemu
diff --git a/Dockerfile.arm64 b/Dockerfile.arm64
index 5572586ae9..4f9d5e1fdc 100644
--- a/Dockerfile.arm64
+++ b/Dockerfile.arm64
@@ -11,6 +11,7 @@ RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-
&& curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& cd jellyfin-web-* \
&& npm ci --no-audit --unsafe-perm \
+ && npm run build:production \
&& mv dist /dist
FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu
diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs
index 7b1f942c5a..86db363374 100644
--- a/Emby.Dlna/PlayTo/PlayToController.cs
+++ b/Emby.Dlna/PlayTo/PlayToController.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
#pragma warning disable CS1591
using System;
@@ -66,7 +64,8 @@ namespace Emby.Dlna.PlayTo
IUserDataManager userDataManager,
ILocalizationManager localization,
IMediaSourceManager mediaSourceManager,
- IMediaEncoder mediaEncoder)
+ IMediaEncoder mediaEncoder,
+ Device device)
{
_session = session;
_sessionManager = sessionManager;
@@ -82,14 +81,7 @@ namespace Emby.Dlna.PlayTo
_localization = localization;
_mediaSourceManager = mediaSourceManager;
_mediaEncoder = mediaEncoder;
- }
- public bool IsSessionActive => !_disposed && _device is not null;
-
- public bool SupportsMediaControl => IsSessionActive;
-
- public void Init(Device device)
- {
_device = device;
_device.OnDeviceUnavailable = OnDeviceUnavailable;
_device.PlaybackStart += OnDevicePlaybackStart;
@@ -102,6 +94,10 @@ namespace Emby.Dlna.PlayTo
_deviceDiscovery.DeviceLeft += OnDeviceDiscoveryDeviceLeft;
}
+ public bool IsSessionActive => !_disposed;
+
+ public bool SupportsMediaControl => IsSessionActive;
+
/*
* Send a message to the DLNA device to notify what is the next track in the playlist.
*/
@@ -131,22 +127,22 @@ namespace Emby.Dlna.PlayTo
}
}
- private void OnDeviceDiscoveryDeviceLeft(object sender, GenericEventArgs e)
+ private void OnDeviceDiscoveryDeviceLeft(object? sender, GenericEventArgs e)
{
var info = e.Argument;
if (!_disposed
- && info.Headers.TryGetValue("USN", out string usn)
+ && info.Headers.TryGetValue("USN", out string? usn)
&& usn.IndexOf(_device.Properties.UUID, StringComparison.OrdinalIgnoreCase) != -1
&& (usn.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) != -1
- || (info.Headers.TryGetValue("NT", out string nt)
+ || (info.Headers.TryGetValue("NT", out string? nt)
&& nt.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) != -1)))
{
OnDeviceUnavailable();
}
}
- private async void OnDeviceMediaChanged(object sender, MediaChangedEventArgs e)
+ private async void OnDeviceMediaChanged(object? sender, MediaChangedEventArgs e)
{
if (_disposed || string.IsNullOrEmpty(e.OldMediaInfo.Url))
{
@@ -188,7 +184,7 @@ namespace Emby.Dlna.PlayTo
}
}
- private async void OnDevicePlaybackStopped(object sender, PlaybackStoppedEventArgs e)
+ private async void OnDevicePlaybackStopped(object? sender, PlaybackStoppedEventArgs e)
{
if (_disposed)
{
@@ -257,7 +253,7 @@ namespace Emby.Dlna.PlayTo
}
}
- private async void OnDevicePlaybackStart(object sender, PlaybackStartEventArgs e)
+ private async void OnDevicePlaybackStart(object? sender, PlaybackStartEventArgs e)
{
if (_disposed)
{
@@ -281,7 +277,7 @@ namespace Emby.Dlna.PlayTo
}
}
- private async void OnDevicePlaybackProgress(object sender, PlaybackProgressEventArgs e)
+ private async void OnDevicePlaybackProgress(object? sender, PlaybackProgressEventArgs e)
{
if (_disposed)
{
@@ -486,9 +482,9 @@ namespace Emby.Dlna.PlayTo
private PlaylistItem CreatePlaylistItem(
BaseItem item,
- User user,
+ User? user,
long startPostionTicks,
- string mediaSourceId,
+ string? mediaSourceId,
int? audioStreamIndex,
int? subtitleStreamIndex)
{
@@ -525,7 +521,7 @@ namespace Emby.Dlna.PlayTo
return playlistItem;
}
- private string GetDlnaHeaders(PlaylistItem item)
+ private string? GetDlnaHeaders(PlaylistItem item)
{
var profile = item.Profile;
var streamInfo = item.StreamInfo;
@@ -579,7 +575,7 @@ namespace Emby.Dlna.PlayTo
return null;
}
- private PlaylistItem GetPlaylistItem(BaseItem item, MediaSourceInfo[] mediaSources, DeviceProfile profile, string deviceId, string mediaSourceId, int? audioStreamIndex, int? subtitleStreamIndex)
+ private PlaylistItem GetPlaylistItem(BaseItem item, MediaSourceInfo[] mediaSources, DeviceProfile profile, string deviceId, string? mediaSourceId, int? audioStreamIndex, int? subtitleStreamIndex)
{
if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
{
@@ -696,7 +692,6 @@ namespace Emby.Dlna.PlayTo
_device.MediaChanged -= OnDeviceMediaChanged;
_deviceDiscovery.DeviceLeft -= OnDeviceDiscoveryDeviceLeft;
_device.OnDeviceUnavailable = null;
- _device = null;
_disposed = true;
}
@@ -716,7 +711,7 @@ namespace Emby.Dlna.PlayTo
case GeneralCommandType.ToggleMute:
return _device.ToggleMute(cancellationToken);
case GeneralCommandType.SetAudioStreamIndex:
- if (command.Arguments.TryGetValue("Index", out string index))
+ if (command.Arguments.TryGetValue("Index", out string? index))
{
if (int.TryParse(index, NumberStyles.Integer, CultureInfo.InvariantCulture, out var val))
{
@@ -740,7 +735,7 @@ namespace Emby.Dlna.PlayTo
throw new ArgumentException("SetSubtitleStreamIndex argument cannot be null");
case GeneralCommandType.SetVolume:
- if (command.Arguments.TryGetValue("Volume", out string vol))
+ if (command.Arguments.TryGetValue("Volume", out string? vol))
{
if (int.TryParse(vol, NumberStyles.Integer, CultureInfo.InvariantCulture, out var volume))
{
@@ -865,34 +860,19 @@ namespace Emby.Dlna.PlayTo
throw new ObjectDisposedException(GetType().Name);
}
- if (_device is null)
+ return name switch
{
- return Task.CompletedTask;
- }
-
- if (name == SessionMessageType.Play)
- {
- return SendPlayCommand(data as PlayRequest, cancellationToken);
- }
-
- if (name == SessionMessageType.Playstate)
- {
- return SendPlaystateCommand(data as PlaystateRequest, cancellationToken);
- }
-
- if (name == SessionMessageType.GeneralCommand)
- {
- return SendGeneralCommand(data as GeneralCommand, cancellationToken);
- }
-
- // Not supported or needed right now
- return Task.CompletedTask;
+ SessionMessageType.Play => SendPlayCommand((data as PlayRequest)!, cancellationToken),
+ SessionMessageType.Playstate => SendPlaystateCommand((data as PlaystateRequest)!, cancellationToken),
+ SessionMessageType.GeneralCommand => SendGeneralCommand((data as GeneralCommand)!, cancellationToken),
+ _ => Task.CompletedTask // Not supported or needed right now
+ };
}
private class StreamParams
{
- private MediaSourceInfo _mediaSource;
- private IMediaSourceManager _mediaSourceManager;
+ private MediaSourceInfo? _mediaSource;
+ private IMediaSourceManager? _mediaSourceManager;
public Guid ItemId { get; set; }
@@ -904,17 +884,17 @@ namespace Emby.Dlna.PlayTo
public int? SubtitleStreamIndex { get; set; }
- public string DeviceProfileId { get; set; }
+ public string? DeviceProfileId { get; set; }
- public string DeviceId { get; set; }
+ public string? DeviceId { get; set; }
- public string MediaSourceId { get; set; }
+ public string? MediaSourceId { get; set; }
- public string LiveStreamId { get; set; }
+ public string? LiveStreamId { get; set; }
- public BaseItem Item { get; set; }
+ public BaseItem? Item { get; set; }
- public async Task GetMediaSource(CancellationToken cancellationToken)
+ public async Task GetMediaSource(CancellationToken cancellationToken)
{
if (_mediaSource is not null)
{
@@ -944,8 +924,8 @@ namespace Emby.Dlna.PlayTo
{
var part = parts[i];
- if (string.Equals(part, "audio", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(part, "videos", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(part, "audio", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(part, "videos", StringComparison.OrdinalIgnoreCase))
{
if (Guid.TryParse(parts[i + 1], out var result))
{
diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs
index b9bfad9d9f..241dff5ae3 100644
--- a/Emby.Dlna/PlayTo/PlayToManager.cs
+++ b/Emby.Dlna/PlayTo/PlayToManager.cs
@@ -205,12 +205,11 @@ namespace Emby.Dlna.PlayTo
_userDataManager,
_localization,
_mediaSourceManager,
- _mediaEncoder);
+ _mediaEncoder,
+ device);
sessionInfo.AddController(controller);
- controller.Init(device);
-
var profile = _dlnaManager.GetProfile(device.Properties.ToDeviceIdentification()) ??
_dlnaManager.GetDefaultProfile();
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 485253bf74..d1caa52638 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -80,11 +80,13 @@ using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Controller.SyncPlay;
using MediaBrowser.Controller.TV;
using MediaBrowser.LocalMetadata.Savers;
+using MediaBrowser.MediaEncoding.BdInfo;
using MediaBrowser.MediaEncoding.Subtitles;
using MediaBrowser.Model.Cryptography;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
+using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.System;
@@ -529,6 +531,8 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton();
+ serviceCollection.AddSingleton();
+
serviceCollection.AddSingleton();
serviceCollection.AddSingleton();
diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs
index 1e3c4dea14..961e225e9e 100644
--- a/Emby.Server.Implementations/Channels/ChannelManager.cs
+++ b/Emby.Server.Implementations/Channels/ChannelManager.cs
@@ -157,16 +157,16 @@ namespace Emby.Server.Implementations.Channels
}
///
- public QueryResult GetChannelsInternal(ChannelQuery query)
+ public async Task> GetChannelsInternalAsync(ChannelQuery query)
{
var user = query.UserId.Equals(default)
? null
: _userManager.GetUserById(query.UserId);
- var channels = GetAllChannels()
- .Select(GetChannelEntity)
+ var channels = await GetAllChannelEntitiesAsync()
.OrderBy(i => i.SortName)
- .ToList();
+ .ToListAsync()
+ .ConfigureAwait(false);
if (query.IsRecordingsFolder.HasValue)
{
@@ -226,6 +226,7 @@ namespace Emby.Server.Implementations.Channels
if (user is not null)
{
+ var userId = user.Id.ToString("N", CultureInfo.InvariantCulture);
channels = channels.Where(i =>
{
if (!i.IsVisible(user))
@@ -235,7 +236,7 @@ namespace Emby.Server.Implementations.Channels
try
{
- return GetChannelProvider(i).IsEnabledFor(user.Id.ToString("N", CultureInfo.InvariantCulture));
+ return GetChannelProvider(i).IsEnabledFor(userId);
}
catch
{
@@ -258,7 +259,7 @@ namespace Emby.Server.Implementations.Channels
{
foreach (var item in all)
{
- RefreshLatestChannelItems(GetChannelProvider(item), CancellationToken.None).GetAwaiter().GetResult();
+ await RefreshLatestChannelItems(GetChannelProvider(item), CancellationToken.None).ConfigureAwait(false);
}
}
@@ -269,13 +270,13 @@ namespace Emby.Server.Implementations.Channels
}
///
- public QueryResult GetChannels(ChannelQuery query)
+ public async Task> GetChannelsAsync(ChannelQuery query)
{
var user = query.UserId.Equals(default)
? null
: _userManager.GetUserById(query.UserId);
- var internalResult = GetChannelsInternal(query);
+ var internalResult = await GetChannelsInternalAsync(query).ConfigureAwait(false);
var dtoOptions = new DtoOptions();
@@ -327,9 +328,12 @@ namespace Emby.Server.Implementations.Channels
progress.Report(100);
}
- private Channel GetChannelEntity(IChannel channel)
+ private async IAsyncEnumerable GetAllChannelEntitiesAsync()
{
- return GetChannel(GetInternalChannelId(channel.Name)) ?? GetChannel(channel, CancellationToken.None).GetAwaiter().GetResult();
+ foreach (IChannel channel in GetAllChannels())
+ {
+ yield return GetChannel(GetInternalChannelId(channel.Name)) ?? await GetChannel(channel, CancellationToken.None).ConfigureAwait(false);
+ }
}
private MediaSourceInfo[] GetSavedMediaSources(BaseItem item)
diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
index 05d0a9b794..2e3988f9eb 100644
--- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
@@ -276,25 +276,31 @@ namespace Emby.Server.Implementations.EntryPoints
/// Libraries the update timer callback.
///
/// The state.
- private void LibraryUpdateTimerCallback(object state)
+ private async void LibraryUpdateTimerCallback(object state)
{
+ List foldersAddedTo;
+ List foldersRemovedFrom;
+ List itemsUpdated;
+ List itemsAdded;
+ List itemsRemoved;
lock (_libraryChangedSyncLock)
{
// Remove dupes in case some were saved multiple times
- var foldersAddedTo = _foldersAddedTo
+ foldersAddedTo = _foldersAddedTo
.DistinctBy(x => x.Id)
.ToList();
- var foldersRemovedFrom = _foldersRemovedFrom
+ foldersRemovedFrom = _foldersRemovedFrom
.DistinctBy(x => x.Id)
.ToList();
- var itemsUpdated = _itemsUpdated
+ itemsUpdated = _itemsUpdated
.Where(i => !_itemsAdded.Contains(i))
.DistinctBy(x => x.Id)
.ToList();
- SendChangeNotifications(_itemsAdded.ToList(), itemsUpdated, _itemsRemoved.ToList(), foldersAddedTo, foldersRemovedFrom, CancellationToken.None).GetAwaiter().GetResult();
+ itemsAdded = _itemsAdded.ToList();
+ itemsRemoved = _itemsRemoved.ToList();
if (LibraryUpdateTimer is not null)
{
@@ -308,6 +314,8 @@ namespace Emby.Server.Implementations.EntryPoints
_foldersAddedTo.Clear();
_foldersRemovedFrom.Clear();
}
+
+ await SendChangeNotifications(itemsAdded, itemsUpdated, itemsRemoved, foldersAddedTo, foldersRemovedFrom, CancellationToken.None).ConfigureAwait(false);
}
///
diff --git a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
index e724618b3a..d32759017d 100644
--- a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
@@ -87,29 +87,30 @@ namespace Emby.Server.Implementations.EntryPoints
}
}
- private void UpdateTimerCallback(object? state)
+ private async void UpdateTimerCallback(object? state)
{
+ List>> changes;
lock (_syncLock)
{
// Remove dupes in case some were saved multiple times
- var changes = _changedItems.ToList();
+ changes = _changedItems.ToList();
_changedItems.Clear();
- SendNotifications(changes, CancellationToken.None).GetAwaiter().GetResult();
-
if (_updateTimer is not null)
{
_updateTimer.Dispose();
_updateTimer = null;
}
}
+
+ await SendNotifications(changes, CancellationToken.None).ConfigureAwait(false);
}
private async Task SendNotifications(List>> changes, CancellationToken cancellationToken)
{
- foreach (var pair in changes)
+ foreach ((var key, var value) in changes)
{
- await SendNotifications(pair.Key, pair.Value, cancellationToken).ConfigureAwait(false);
+ await SendNotifications(key, value, cancellationToken).ConfigureAwait(false);
}
}
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 66bd68ddd0..e5c520ca2b 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -113,6 +113,7 @@ namespace Emby.Server.Implementations.Library
/// The image processor.
/// The memory cache.
/// The naming options.
+ /// The directory service.
public LibraryManager(
IServerApplicationHost appHost,
ILoggerFactory loggerFactory,
@@ -128,7 +129,8 @@ namespace Emby.Server.Implementations.Library
IItemRepository itemRepository,
IImageProcessor imageProcessor,
IMemoryCache memoryCache,
- NamingOptions namingOptions)
+ NamingOptions namingOptions,
+ IDirectoryService directoryService)
{
_appHost = appHost;
_logger = loggerFactory.CreateLogger();
@@ -146,7 +148,7 @@ namespace Emby.Server.Implementations.Library
_memoryCache = memoryCache;
_namingOptions = namingOptions;
- _extraResolver = new ExtraResolver(loggerFactory.CreateLogger(), namingOptions);
+ _extraResolver = new ExtraResolver(loggerFactory.CreateLogger(), namingOptions, directoryService);
_configurationManager.ConfigurationUpdated += ConfigurationUpdated;
@@ -537,7 +539,7 @@ namespace Emby.Server.Implementations.Library
collectionType = GetContentTypeOverride(fullPath, true);
}
- var args = new ItemResolveArgs(_configurationManager.ApplicationPaths, directoryService)
+ var args = new ItemResolveArgs(_configurationManager.ApplicationPaths, this)
{
Parent = parent,
FileInfo = fileInfo,
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
index 69e9057984..a74f824752 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
@@ -192,7 +192,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
continue;
}
- if (resolvedItem.Files.Count == 0)
+ // Until multi-part books are handled letting files stack hides them from browsing in the client
+ if (resolvedItem.Files.Count == 0 || resolvedItem.Extras.Count > 0 || resolvedItem.AlternateVersions.Count > 0)
{
continue;
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
index a922e36855..bbc70701cb 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
@@ -25,16 +25,19 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
{
private readonly ILogger _logger;
private readonly NamingOptions _namingOptions;
+ private readonly IDirectoryService _directoryService;
///
/// Initializes a new instance of the class.
///
/// The logger.
/// The naming options.
- public MusicAlbumResolver(ILogger logger, NamingOptions namingOptions)
+ /// The directory service.
+ public MusicAlbumResolver(ILogger logger, NamingOptions namingOptions, IDirectoryService directoryService)
{
_logger = logger;
_namingOptions = namingOptions;
+ _directoryService = directoryService;
}
///
@@ -109,7 +112,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
}
// If args contains music it's a music album
- if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService))
+ if (ContainsMusic(args.FileSystemChildren, true, _directoryService))
{
return true;
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
index 2538c2b5b4..c858dc53d9 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
@@ -6,6 +6,7 @@ using System.Threading.Tasks;
using Emby.Naming.Common;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
using Microsoft.Extensions.Logging;
@@ -18,19 +19,23 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
public class MusicArtistResolver : ItemResolver
{
private readonly ILogger _logger;
- private NamingOptions _namingOptions;
+ private readonly NamingOptions _namingOptions;
+ private readonly IDirectoryService _directoryService;
///
/// Initializes a new instance of the class.
///
/// Instance of the interface.
/// The .
+ /// The directory service.
public MusicArtistResolver(
ILogger logger,
- NamingOptions namingOptions)
+ NamingOptions namingOptions,
+ IDirectoryService directoryService)
{
_logger = logger;
_namingOptions = namingOptions;
+ _directoryService = directoryService;
}
///
@@ -78,9 +83,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
return null;
}
- var directoryService = args.DirectoryService;
-
- var albumResolver = new MusicAlbumResolver(_logger, _namingOptions);
+ var albumResolver = new MusicAlbumResolver(_logger, _namingOptions, _directoryService);
var directories = args.FileSystemChildren.Where(i => i.IsDirectory);
@@ -97,7 +100,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
}
// If we contain a music album assume we are an artist folder
- if (albumResolver.IsMusicAlbum(fileSystemInfo.FullName, directoryService))
+ if (albumResolver.IsMusicAlbum(fileSystemInfo.FullName, _directoryService))
{
// Stop once we see a music album
state.Stop();
diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
index e8615e7db7..4fac91bf1e 100644
--- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
@@ -25,14 +25,17 @@ namespace Emby.Server.Implementations.Library.Resolvers
{
private readonly ILogger _logger;
- protected BaseVideoResolver(ILogger logger, NamingOptions namingOptions)
+ protected BaseVideoResolver(ILogger logger, NamingOptions namingOptions, IDirectoryService directoryService)
{
_logger = logger;
NamingOptions = namingOptions;
+ DirectoryService = directoryService;
}
protected NamingOptions NamingOptions { get; }
+ protected IDirectoryService DirectoryService { get; }
+
///
/// Resolves the specified args.
///
@@ -65,13 +68,25 @@ namespace Emby.Server.Implementations.Library.Resolvers
var filename = child.Name;
if (child.IsDirectory)
{
- if (IsDvdDirectory(child.FullName, filename, args.DirectoryService))
+ if (IsDvdDirectory(child.FullName, filename, DirectoryService))
{
- videoType = VideoType.Dvd;
+ var videoTmp = new TVideoType
+ {
+ Path = args.Path,
+ VideoType = VideoType.Dvd
+ };
+ Set3DFormat(videoTmp);
+ return videoTmp;
}
else if (IsBluRayDirectory(filename))
{
- videoType = VideoType.BluRay;
+ var videoTmp = new TVideoType
+ {
+ Path = args.Path,
+ VideoType = VideoType.BluRay
+ };
+ Set3DFormat(videoTmp);
+ return videoTmp;
}
}
else if (IsDvdFile(filename))
diff --git a/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs b/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs
index 30c52e19d3..0b255f673b 100644
--- a/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs
@@ -4,6 +4,7 @@ using System.IO;
using Emby.Naming.Common;
using Emby.Naming.Video;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
using Microsoft.Extensions.Logging;
@@ -25,11 +26,12 @@ namespace Emby.Server.Implementations.Library.Resolvers
///
/// The logger.
/// An instance of .
- public ExtraResolver(ILogger logger, NamingOptions namingOptions)
+ /// The directory service.
+ public ExtraResolver(ILogger logger, NamingOptions namingOptions, IDirectoryService directoryService)
{
_namingOptions = namingOptions;
- _trailerResolvers = new IItemResolver[] { new GenericVideoResolver(logger, namingOptions) };
- _videoResolvers = new IItemResolver[] { new GenericVideoResolver