mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-06-04 06:48:35 +01:00
Merge branch 'master' into displaypreferences-efcore
This commit is contained in:
@@ -192,7 +192,7 @@ namespace Emby.Server.Implementations
|
||||
/// Gets or sets the application paths.
|
||||
/// </summary>
|
||||
/// <value>The application paths.</value>
|
||||
protected ServerApplicationPaths ApplicationPaths { get; set; }
|
||||
protected IServerApplicationPaths ApplicationPaths { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets all concrete types.
|
||||
@@ -236,7 +236,7 @@ namespace Emby.Server.Implementations
|
||||
/// Initializes a new instance of the <see cref="ApplicationHost" /> class.
|
||||
/// </summary>
|
||||
public ApplicationHost(
|
||||
ServerApplicationPaths applicationPaths,
|
||||
IServerApplicationPaths applicationPaths,
|
||||
ILoggerFactory loggerFactory,
|
||||
IStartupOptions options,
|
||||
IFileSystem fileSystem,
|
||||
@@ -792,7 +792,6 @@ namespace Emby.Server.Implementations
|
||||
Resolve<IMediaSourceManager>().AddParts(GetExports<IMediaSourceProvider>());
|
||||
|
||||
Resolve<INotificationManager>().AddParts(GetExports<INotificationService>(), GetExports<INotificationTypeFactory>());
|
||||
Resolve<IUserManager>().AddParts(GetExports<IAuthenticationProvider>(), GetExports<IPasswordResetProvider>());
|
||||
|
||||
Resolve<IIsoManager>().AddParts(GetExports<IIsoMounter>());
|
||||
}
|
||||
|
||||
@@ -1111,7 +1111,8 @@ namespace Emby.Server.Implementations.Data
|
||||
continue;
|
||||
}
|
||||
|
||||
str.Append(ToValueString(i) + "|");
|
||||
str.Append(ToValueString(i))
|
||||
.Append('|');
|
||||
}
|
||||
|
||||
str.Length -= 1; // Remove last |
|
||||
@@ -2472,7 +2473,7 @@ namespace Emby.Server.Implementations.Data
|
||||
var item = query.SimilarTo;
|
||||
|
||||
var builder = new StringBuilder();
|
||||
builder.Append("(");
|
||||
builder.Append('(');
|
||||
|
||||
if (string.IsNullOrEmpty(item.OfficialRating))
|
||||
{
|
||||
@@ -2510,7 +2511,7 @@ namespace Emby.Server.Implementations.Data
|
||||
if (!string.IsNullOrEmpty(query.SearchTerm))
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
builder.Append("(");
|
||||
builder.Append('(');
|
||||
|
||||
builder.Append("((CleanName like @SearchTermStartsWith or (OriginalTitle not null and OriginalTitle like @SearchTermStartsWith)) * 10)");
|
||||
|
||||
@@ -5239,7 +5240,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
insertText.Append(",");
|
||||
insertText.Append(',');
|
||||
}
|
||||
|
||||
insertText.AppendFormat("(@ItemId, @AncestorId{0}, @AncestorIdText{0})", i.ToString(CultureInfo.InvariantCulture));
|
||||
@@ -6332,7 +6333,10 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
|
||||
|
||||
foreach (var column in _mediaAttachmentSaveColumns.Skip(1))
|
||||
{
|
||||
insertText.Append("@" + column + index + ",");
|
||||
insertText.Append('@')
|
||||
.Append(column)
|
||||
.Append(index)
|
||||
.Append(',');
|
||||
}
|
||||
|
||||
insertText.Length -= 1;
|
||||
|
||||
@@ -34,10 +34,10 @@
|
||||
<PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.6" />
|
||||
<PackageReference Include="Mono.Nat" Version="2.0.1" />
|
||||
<PackageReference Include="prometheus-net.DotNetRuntime" Version="3.3.1" />
|
||||
<PackageReference Include="ServiceStack.Text.Core" Version="5.9.0" />
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Emby.Server.Implementations.Udp;
|
||||
@@ -48,8 +49,16 @@ namespace Emby.Server.Implementations.EntryPoints
|
||||
/// <inheritdoc />
|
||||
public Task RunAsync()
|
||||
{
|
||||
_udpServer = new UdpServer(_logger, _appHost, _config);
|
||||
_udpServer.Start(PortNumber, _cancellationTokenSource.Token);
|
||||
try
|
||||
{
|
||||
_udpServer = new UdpServer(_logger, _appHost, _config);
|
||||
_udpServer.Start(PortNumber, _cancellationTokenSource.Token);
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Unable to start AutoDiscovery listener on UDP port {PortNumber}", PortNumber);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
@@ -245,6 +245,16 @@ namespace Emby.Server.Implementations.IO
|
||||
if (info is FileInfo fileInfo)
|
||||
{
|
||||
result.Length = fileInfo.Length;
|
||||
|
||||
// Issue #2354 get the size of files behind symbolic links
|
||||
if (fileInfo.Attributes.HasFlag(FileAttributes.ReparsePoint))
|
||||
{
|
||||
using (Stream thisFileStream = File.OpenRead(fileInfo.FullName))
|
||||
{
|
||||
result.Length = thisFileStream.Length;
|
||||
}
|
||||
}
|
||||
|
||||
result.DirectoryName = fileInfo.DirectoryName;
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace Emby.Server.Implementations.Library
|
||||
if (parent != null)
|
||||
{
|
||||
// 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))
|
||||
{
|
||||
return true;
|
||||
|
||||
@@ -11,6 +11,17 @@ namespace Emby.Server.Implementations.Library
|
||||
{
|
||||
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 string OriginalStreamId { get; set; }
|
||||
@@ -21,18 +32,7 @@ namespace Emby.Server.Implementations.Library
|
||||
|
||||
public MediaSourceInfo MediaSource { get; set; }
|
||||
|
||||
public string UniqueId { get; private set; }
|
||||
|
||||
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 string UniqueId { get; }
|
||||
|
||||
public Task Close()
|
||||
{
|
||||
|
||||
@@ -59,6 +59,8 @@ namespace Emby.Server.Implementations.Library
|
||||
/// </summary>
|
||||
public class LibraryManager : ILibraryManager
|
||||
{
|
||||
private const string ShortcutFileExtension = ".mblink";
|
||||
|
||||
private readonly ILogger<LibraryManager> _logger;
|
||||
private readonly ITaskManager _taskManager;
|
||||
private readonly IUserManager _userManager;
|
||||
@@ -74,63 +76,24 @@ namespace Emby.Server.Implementations.Library
|
||||
private readonly ConcurrentDictionary<Guid, BaseItem> _libraryItemsCache;
|
||||
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 string[] _videoFileExtensions;
|
||||
|
||||
private ILibraryMonitor LibraryMonitor => _libraryMonitorFactory.Value;
|
||||
|
||||
private IProviderManager ProviderManager => _providerManagerFactory.Value;
|
||||
|
||||
private IUserViewManager UserViewManager => _userviewManagerFactory.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the postscan tasks.
|
||||
/// The _root folder.
|
||||
/// </summary>
|
||||
/// <value>The postscan tasks.</value>
|
||||
private ILibraryPostScanTask[] PostscanTasks { get; set; }
|
||||
private volatile AggregateFolder _rootFolder;
|
||||
private volatile UserRootFolder _userRootFolder;
|
||||
|
||||
/// <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; }
|
||||
|
||||
/// <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; }
|
||||
private bool _wizardCompleted;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LibraryManager" /> class.
|
||||
@@ -185,37 +148,19 @@ namespace Emby.Server.Implementations.Library
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the parts.
|
||||
/// Occurs when [item added].
|
||||
/// </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();
|
||||
}
|
||||
public event EventHandler<ItemChangeEventArgs> ItemAdded;
|
||||
|
||||
/// <summary>
|
||||
/// The _root folder.
|
||||
/// Occurs when [item updated].
|
||||
/// </summary>
|
||||
private volatile AggregateFolder _rootFolder;
|
||||
public event EventHandler<ItemChangeEventArgs> ItemUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// The _root folder sync lock.
|
||||
/// Occurs when [item removed].
|
||||
/// </summary>
|
||||
private readonly object _rootFolderSyncLock = new object();
|
||||
public event EventHandler<ItemChangeEventArgs> ItemRemoved;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the root folder.
|
||||
@@ -240,7 +185,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>
|
||||
/// Records the configuration values.
|
||||
@@ -340,7 +346,7 @@ namespace Emby.Server.Implementations.Library
|
||||
if (item is LiveTvProgram)
|
||||
{
|
||||
_logger.LogDebug(
|
||||
"Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
|
||||
"Removing item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
|
||||
item.GetType().Name,
|
||||
item.Name ?? "Unknown name",
|
||||
item.Path ?? string.Empty,
|
||||
@@ -349,7 +355,7 @@ namespace Emby.Server.Implementations.Library
|
||||
else
|
||||
{
|
||||
_logger.LogInformation(
|
||||
"Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
|
||||
"Removing item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
|
||||
item.GetType().Name,
|
||||
item.Name ?? "Unknown name",
|
||||
item.Path ?? string.Empty,
|
||||
@@ -367,7 +373,12 @@ namespace Emby.Server.Implementations.Library
|
||||
continue;
|
||||
}
|
||||
|
||||
_logger.LogDebug("Deleting path {MetadataPath}", metadataPath);
|
||||
_logger.LogDebug(
|
||||
"Deleting metadata path, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
|
||||
item.GetType().Name,
|
||||
item.Name ?? "Unknown name",
|
||||
metadataPath,
|
||||
item.Id);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -391,7 +402,13 @@ namespace Emby.Server.Implementations.Library
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogDebug("Deleting path {path}", fileSystemInfo.FullName);
|
||||
_logger.LogInformation(
|
||||
"Deleting item path, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
|
||||
item.GetType().Name,
|
||||
item.Name ?? "Unknown name",
|
||||
fileSystemInfo.FullName,
|
||||
item.Id);
|
||||
|
||||
if (fileSystemInfo.IsDirectory)
|
||||
{
|
||||
Directory.Delete(fileSystemInfo.FullName, true);
|
||||
@@ -500,7 +517,7 @@ namespace Emby.Server.Implementations.Library
|
||||
// Try to normalize paths located underneath program-data in an attempt to make them more portable
|
||||
key = key.Substring(_configurationManager.ApplicationPaths.ProgramDataPath.Length)
|
||||
.TrimStart(new[] { '/', '\\' })
|
||||
.Replace("/", "\\");
|
||||
.Replace('/', '\\');
|
||||
}
|
||||
|
||||
if (forceCaseInsensitive || !_configurationManager.Configuration.EnableCaseSensitiveItemIds)
|
||||
@@ -763,14 +780,11 @@ namespace Emby.Server.Implementations.Library
|
||||
return rootFolder;
|
||||
}
|
||||
|
||||
private volatile UserRootFolder _userRootFolder;
|
||||
private readonly object _syncLock = new object();
|
||||
|
||||
public Folder GetUserRootFolder()
|
||||
{
|
||||
if (_userRootFolder == null)
|
||||
{
|
||||
lock (_syncLock)
|
||||
lock (_userRootFolderSyncLock)
|
||||
{
|
||||
if (_userRootFolder == null)
|
||||
{
|
||||
@@ -1320,7 +1334,7 @@ namespace Emby.Server.Implementations.Library
|
||||
|
||||
return new QueryResult<BaseItem>
|
||||
{
|
||||
Items = _itemRepository.GetItemList(query).ToArray()
|
||||
Items = _itemRepository.GetItemList(query)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1451,11 +1465,9 @@ namespace Emby.Server.Implementations.Library
|
||||
return _itemRepository.GetItems(query);
|
||||
}
|
||||
|
||||
var list = _itemRepository.GetItemList(query);
|
||||
|
||||
return new QueryResult<BaseItem>
|
||||
{
|
||||
Items = list
|
||||
Items = _itemRepository.GetItemList(query)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1864,7 +1876,8 @@ namespace Emby.Server.Implementations.Library
|
||||
}
|
||||
|
||||
var outdated = forceUpdate ? item.ImageInfos.Where(i => i.Path != null).ToArray() : item.ImageInfos.Where(ImageNeedsRefresh).ToArray();
|
||||
if (outdated.Length == 0)
|
||||
// Skip image processing if current or live tv source
|
||||
if (outdated.Length == 0 || item.SourceType != SourceType.Library)
|
||||
{
|
||||
RegisterItem(item);
|
||||
return;
|
||||
@@ -1933,12 +1946,9 @@ namespace Emby.Server.Implementations.Library
|
||||
/// <summary>
|
||||
/// Updates the item.
|
||||
/// </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
|
||||
var itemsList = items.ToList();
|
||||
|
||||
foreach (var item in itemsList)
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (item.IsFileProtocol)
|
||||
{
|
||||
@@ -1950,11 +1960,11 @@ namespace Emby.Server.Implementations.Library
|
||||
UpdateImages(item, updateReason >= ItemUpdateType.ImageUpdate);
|
||||
}
|
||||
|
||||
_itemRepository.SaveItems(itemsList, cancellationToken);
|
||||
_itemRepository.SaveItems(items, cancellationToken);
|
||||
|
||||
if (ItemUpdated != null)
|
||||
{
|
||||
foreach (var item in itemsList)
|
||||
foreach (var item in items)
|
||||
{
|
||||
// With the live tv guide this just creates too much noise
|
||||
if (item.SourceType != SourceType.Library)
|
||||
@@ -2177,8 +2187,6 @@ namespace Emby.Server.Implementations.Library
|
||||
.FirstOrDefault(i => !string.IsNullOrEmpty(i));
|
||||
}
|
||||
|
||||
private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromHours(24);
|
||||
|
||||
public UserView GetNamedView(
|
||||
User user,
|
||||
string name,
|
||||
@@ -2476,14 +2484,9 @@ namespace Emby.Server.Implementations.Library
|
||||
|
||||
var isFolder = episode.VideoType == VideoType.BluRay || episode.VideoType == VideoType.Dvd;
|
||||
|
||||
var episodeInfo = episode.IsFileProtocol ?
|
||||
resolver.Resolve(episode.Path, isFolder, null, null, isAbsoluteNaming) :
|
||||
new Naming.TV.EpisodeInfo();
|
||||
|
||||
if (episodeInfo == null)
|
||||
{
|
||||
episodeInfo = new Naming.TV.EpisodeInfo();
|
||||
}
|
||||
var episodeInfo = episode.IsFileProtocol
|
||||
? resolver.Resolve(episode.Path, isFolder, null, null, isAbsoluteNaming) ?? new Naming.TV.EpisodeInfo()
|
||||
: new Naming.TV.EpisodeInfo();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -2491,11 +2494,13 @@ namespace Emby.Server.Implementations.Library
|
||||
if (libraryOptions.EnableEmbeddedEpisodeInfos && string.Equals(episodeInfo.Container, "mp4", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Read from metadata
|
||||
var mediaInfo = _mediaEncoder.GetMediaInfo(new MediaInfoRequest
|
||||
{
|
||||
MediaSource = episode.GetMediaSources(false)[0],
|
||||
MediaType = DlnaProfileType.Video
|
||||
}, CancellationToken.None).GetAwaiter().GetResult();
|
||||
var mediaInfo = _mediaEncoder.GetMediaInfo(
|
||||
new MediaInfoRequest
|
||||
{
|
||||
MediaSource = episode.GetMediaSources(false)[0],
|
||||
MediaType = DlnaProfileType.Video
|
||||
},
|
||||
CancellationToken.None).GetAwaiter().GetResult();
|
||||
if (mediaInfo.ParentIndexNumber > 0)
|
||||
{
|
||||
episodeInfo.SeasonNumber = mediaInfo.ParentIndexNumber;
|
||||
@@ -2653,7 +2658,7 @@ namespace Emby.Server.Implementations.Library
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -2670,9 +2675,7 @@ namespace Emby.Server.Implementations.Library
|
||||
.Select(video =>
|
||||
{
|
||||
// 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 (dbItem != null)
|
||||
if (GetItemById(video.Id) is Trailer dbItem)
|
||||
{
|
||||
video = dbItem;
|
||||
}
|
||||
@@ -2999,23 +3002,6 @@ namespace Emby.Server.Implementations.Library
|
||||
});
|
||||
}
|
||||
|
||||
private static bool ValidateNetworkPath(string path)
|
||||
{
|
||||
// if (Environment.OSVersion.Platform == PlatformID.Win32NT)
|
||||
//{
|
||||
// // We can't validate protocol-based paths, so just allow them
|
||||
// if (path.IndexOf("://", StringComparison.OrdinalIgnoreCase) == -1)
|
||||
// {
|
||||
// return Directory.Exists(path);
|
||||
// }
|
||||
//}
|
||||
|
||||
// Without native support for unc, we cannot validate this when running under mono
|
||||
return true;
|
||||
}
|
||||
|
||||
private const string ShortcutFileExtension = ".mblink";
|
||||
|
||||
public void AddMediaPath(string virtualFolderName, MediaPathInfo pathInfo)
|
||||
{
|
||||
AddMediaPathInternal(virtualFolderName, pathInfo, true);
|
||||
@@ -3040,11 +3026,6 @@ namespace Emby.Server.Implementations.Library
|
||||
throw new FileNotFoundException("The path does not exist.");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(pathInfo.NetworkPath) && !ValidateNetworkPath(pathInfo.NetworkPath))
|
||||
{
|
||||
throw new FileNotFoundException("The network path does not exist.");
|
||||
}
|
||||
|
||||
var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
|
||||
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
|
||||
|
||||
@@ -3083,11 +3064,6 @@ namespace Emby.Server.Implementations.Library
|
||||
throw new ArgumentNullException(nameof(pathInfo));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(pathInfo.NetworkPath) && !ValidateNetworkPath(pathInfo.NetworkPath))
|
||||
{
|
||||
throw new FileNotFoundException("The network path does not exist.");
|
||||
}
|
||||
|
||||
var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
|
||||
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
|
||||
|
||||
@@ -3219,7 +3195,8 @@ namespace Emby.Server.Implementations.Library
|
||||
|
||||
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)
|
||||
|
||||
@@ -23,9 +23,8 @@ namespace Emby.Server.Implementations.Library
|
||||
{
|
||||
private readonly IMediaEncoder _mediaEncoder;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private IJsonSerializer _json;
|
||||
private IApplicationPaths _appPaths;
|
||||
private readonly IJsonSerializer _json;
|
||||
private readonly IApplicationPaths _appPaths;
|
||||
|
||||
public LiveStreamHelper(IMediaEncoder mediaEncoder, ILogger logger, IJsonSerializer json, IApplicationPaths appPaths)
|
||||
{
|
||||
@@ -72,13 +71,14 @@ namespace Emby.Server.Implementations.Library
|
||||
|
||||
mediaSource.AnalyzeDurationMs = 3000;
|
||||
|
||||
mediaInfo = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest
|
||||
{
|
||||
MediaSource = mediaSource,
|
||||
MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video,
|
||||
ExtractChapters = false
|
||||
|
||||
}, cancellationToken).ConfigureAwait(false);
|
||||
mediaInfo = await _mediaEncoder.GetMediaInfo(
|
||||
new MediaInfoRequest
|
||||
{
|
||||
MediaSource = mediaSource,
|
||||
MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video,
|
||||
ExtractChapters = false
|
||||
},
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (cacheFilePath != null)
|
||||
{
|
||||
@@ -126,7 +126,7 @@ namespace Emby.Server.Implementations.Library
|
||||
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)
|
||||
{
|
||||
@@ -137,7 +137,7 @@ namespace Emby.Server.Implementations.Library
|
||||
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.BitRate.HasValue)
|
||||
|
||||
@@ -29,6 +29,9 @@ namespace Emby.Server.Implementations.Library
|
||||
{
|
||||
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 IUserManager _userManager;
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
@@ -40,6 +43,11 @@ namespace Emby.Server.Implementations.Library
|
||||
private readonly ILocalizationManager _localizationManager;
|
||||
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;
|
||||
|
||||
public MediaSourceManager(
|
||||
@@ -368,7 +376,6 @@ namespace Emby.Server.Implementations.Library
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var preferredSubs = string.IsNullOrEmpty(user.SubtitleLanguagePreference)
|
||||
? Array.Empty<string>() : NormalizeLanguage(user.SubtitleLanguagePreference);
|
||||
|
||||
@@ -451,9 +458,6 @@ namespace Emby.Server.Implementations.Library
|
||||
.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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (string.IsNullOrEmpty(key))
|
||||
@@ -881,9 +882,9 @@ namespace Emby.Server.Implementations.Library
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private readonly object _disposeLock = new object();
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace Emby.Server.Implementations.Library
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
|
||||
@@ -20,13 +20,11 @@ namespace Emby.Server.Implementations.Library
|
||||
{
|
||||
public class SearchEngine : ISearchEngine
|
||||
{
|
||||
private readonly ILogger<SearchEngine> _logger;
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly IUserManager _userManager;
|
||||
|
||||
public SearchEngine(ILogger<SearchEngine> logger, ILibraryManager libraryManager, IUserManager userManager)
|
||||
public SearchEngine(ILibraryManager libraryManager, IUserManager userManager)
|
||||
{
|
||||
_logger = logger;
|
||||
_libraryManager = libraryManager;
|
||||
_userManager = userManager;
|
||||
}
|
||||
@@ -34,11 +32,7 @@ namespace Emby.Server.Implementations.Library
|
||||
public QueryResult<SearchHintInfo> GetSearchHints(SearchQuery query)
|
||||
{
|
||||
User user = null;
|
||||
|
||||
if (query.UserId.Equals(Guid.Empty))
|
||||
{
|
||||
}
|
||||
else
|
||||
if (query.UserId != Guid.Empty)
|
||||
{
|
||||
user = _userManager.GetUserById(query.UserId);
|
||||
}
|
||||
@@ -48,19 +42,19 @@ namespace Emby.Server.Implementations.Library
|
||||
|
||||
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)
|
||||
{
|
||||
results = results.Take(query.Limit.Value).ToList();
|
||||
results = results.GetRange(0, query.Limit.Value);
|
||||
}
|
||||
|
||||
return new QueryResult<SearchHintInfo>
|
||||
{
|
||||
TotalRecordCount = totalRecordCount,
|
||||
|
||||
Items = results.ToArray()
|
||||
Items = results
|
||||
};
|
||||
}
|
||||
|
||||
@@ -85,7 +79,7 @@ namespace Emby.Server.Implementations.Library
|
||||
|
||||
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();
|
||||
|
||||
@@ -4,6 +4,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Emby.Server.Implementations.Library;
|
||||
@@ -28,7 +29,6 @@ using MediaBrowser.Model.Globalization;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.LiveTv;
|
||||
using MediaBrowser.Model.Querying;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Episode = MediaBrowser.Controller.Entities.TV.Episode;
|
||||
@@ -54,7 +54,6 @@ namespace Emby.Server.Implementations.LiveTv
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly ITaskManager _taskManager;
|
||||
private readonly ILocalizationManager _localization;
|
||||
private readonly IJsonSerializer _jsonSerializer;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly IChannelManager _channelManager;
|
||||
private readonly LiveTvDtoService _tvDtoService;
|
||||
@@ -73,7 +72,6 @@ namespace Emby.Server.Implementations.LiveTv
|
||||
ILibraryManager libraryManager,
|
||||
ITaskManager taskManager,
|
||||
ILocalizationManager localization,
|
||||
IJsonSerializer jsonSerializer,
|
||||
IFileSystem fileSystem,
|
||||
IChannelManager channelManager,
|
||||
LiveTvDtoService liveTvDtoService)
|
||||
@@ -85,7 +83,6 @@ namespace Emby.Server.Implementations.LiveTv
|
||||
_libraryManager = libraryManager;
|
||||
_taskManager = taskManager;
|
||||
_localization = localization;
|
||||
_jsonSerializer = jsonSerializer;
|
||||
_fileSystem = fileSystem;
|
||||
_dtoService = dtoService;
|
||||
_userDataManager = userDataManager;
|
||||
@@ -2234,7 +2231,7 @@ namespace Emby.Server.Implementations.LiveTv
|
||||
|
||||
public async Task<TunerHostInfo> SaveTunerHost(TunerHostInfo info, bool dataSourceChanged = true)
|
||||
{
|
||||
info = _jsonSerializer.DeserializeFromString<TunerHostInfo>(_jsonSerializer.SerializeToString(info));
|
||||
info = JsonSerializer.Deserialize<TunerHostInfo>(JsonSerializer.Serialize(info));
|
||||
|
||||
var provider = _tunerHosts.FirstOrDefault(i => string.Equals(info.Type, i.Type, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
@@ -2278,7 +2275,7 @@ namespace Emby.Server.Implementations.LiveTv
|
||||
{
|
||||
// Hack to make the object a pure ListingsProviderInfo instead of an AddListingProvider
|
||||
// ServerConfiguration.SaveConfiguration crashes during xml serialization for AddListingProvider
|
||||
info = _jsonSerializer.DeserializeFromString<ListingsProviderInfo>(_jsonSerializer.SerializeToString(info));
|
||||
info = JsonSerializer.Deserialize<ListingsProviderInfo>(JsonSerializer.Serialize(info));
|
||||
|
||||
var provider = _listingProviders.FirstOrDefault(i => string.Equals(info.Type, i.Type, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"AppDeviceValues": "App: {0}, Gerät: {1}",
|
||||
"Application": "Anwendung",
|
||||
"Artists": "Interpreten",
|
||||
"AuthenticationSucceededWithUserName": "{0} hat sich erfolgreich authentifiziert",
|
||||
"AuthenticationSucceededWithUserName": "{0} hat sich erfolgreich angemeldet",
|
||||
"Books": "Bücher",
|
||||
"CameraImageUploadedFrom": "Ein neues Foto wurde von {0} hochgeladen",
|
||||
"Channels": "Kanäle",
|
||||
|
||||
@@ -5,47 +5,47 @@
|
||||
"Artists": "Artis",
|
||||
"AuthenticationSucceededWithUserName": "{0} berjaya disahkan",
|
||||
"Books": "Buku-buku",
|
||||
"CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
|
||||
"CameraImageUploadedFrom": "Ada gambar dari kamera yang baru dimuat naik melalui {0}",
|
||||
"Channels": "Saluran",
|
||||
"ChapterNameValue": "Chapter {0}",
|
||||
"ChapterNameValue": "Bab {0}",
|
||||
"Collections": "Koleksi",
|
||||
"DeviceOfflineWithName": "{0} has disconnected",
|
||||
"DeviceOnlineWithName": "{0} is connected",
|
||||
"DeviceOfflineWithName": "{0} telah diputuskan sambungan",
|
||||
"DeviceOnlineWithName": "{0} telah disambung",
|
||||
"FailedLoginAttemptWithUserName": "Cubaan log masuk gagal dari {0}",
|
||||
"Favorites": "Favorites",
|
||||
"Folders": "Folders",
|
||||
"Favorites": "Kegemaran",
|
||||
"Folders": "Fail-fail",
|
||||
"Genres": "Genre-genre",
|
||||
"HeaderAlbumArtists": "Album Artists",
|
||||
"HeaderAlbumArtists": "Album Artis-artis",
|
||||
"HeaderCameraUploads": "Muatnaik Kamera",
|
||||
"HeaderContinueWatching": "Terus Menonton",
|
||||
"HeaderFavoriteAlbums": "Favorite Albums",
|
||||
"HeaderFavoriteArtists": "Favorite Artists",
|
||||
"HeaderFavoriteEpisodes": "Favorite Episodes",
|
||||
"HeaderFavoriteShows": "Favorite Shows",
|
||||
"HeaderFavoriteSongs": "Favorite Songs",
|
||||
"HeaderLiveTV": "Live TV",
|
||||
"HeaderNextUp": "Next Up",
|
||||
"HeaderRecordingGroups": "Recording Groups",
|
||||
"HomeVideos": "Home videos",
|
||||
"Inherit": "Inherit",
|
||||
"ItemAddedWithName": "{0} was added to the library",
|
||||
"ItemRemovedWithName": "{0} was removed from the library",
|
||||
"HeaderFavoriteAlbums": "Album-album Kegemaran",
|
||||
"HeaderFavoriteArtists": "Artis-artis Kegemaran",
|
||||
"HeaderFavoriteEpisodes": "Episod-episod Kegemaran",
|
||||
"HeaderFavoriteShows": "Rancangan-rancangan Kegemaran",
|
||||
"HeaderFavoriteSongs": "Lagu-lagu Kegemaran",
|
||||
"HeaderLiveTV": "TV Siaran Langsung",
|
||||
"HeaderNextUp": "Seterusnya",
|
||||
"HeaderRecordingGroups": "Kumpulan-kumpulan Rakaman",
|
||||
"HomeVideos": "Video Personal",
|
||||
"Inherit": "Mewarisi",
|
||||
"ItemAddedWithName": "{0} telah ditambahkan ke dalam pustaka",
|
||||
"ItemRemovedWithName": "{0} telah dibuang daripada pustaka",
|
||||
"LabelIpAddressValue": "Alamat IP: {0}",
|
||||
"LabelRunningTimeValue": "Running time: {0}",
|
||||
"Latest": "Latest",
|
||||
"MessageApplicationUpdated": "Jellyfin Server has been updated",
|
||||
"MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
|
||||
"MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
|
||||
"MessageServerConfigurationUpdated": "Server configuration has been updated",
|
||||
"MixedContent": "Mixed content",
|
||||
"Movies": "Movies",
|
||||
"LabelRunningTimeValue": "Masa berjalan: {0}",
|
||||
"Latest": "Terbaru",
|
||||
"MessageApplicationUpdated": "Jellyfin Server telah dikemas kini",
|
||||
"MessageApplicationUpdatedTo": "Jellyfin Server telah dikemas kini ke {0}",
|
||||
"MessageNamedServerConfigurationUpdatedWithValue": "Konfigurasi pelayan di bahagian {0} telah dikemas kini",
|
||||
"MessageServerConfigurationUpdated": "Konfigurasi pelayan telah dikemas kini",
|
||||
"MixedContent": "Kandungan campuran",
|
||||
"Movies": "Filem",
|
||||
"Music": "Muzik",
|
||||
"MusicVideos": "Video muzik",
|
||||
"NameInstallFailed": "{0} installation failed",
|
||||
"NameSeasonNumber": "Season {0}",
|
||||
"NameSeasonUnknown": "Season Unknown",
|
||||
"NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.",
|
||||
"NotificationOptionApplicationUpdateAvailable": "Application update available",
|
||||
"NameInstallFailed": "{0} pemasangan gagal",
|
||||
"NameSeasonNumber": "Musim {0}",
|
||||
"NameSeasonUnknown": "Musim Tidak Diketahui",
|
||||
"NewVersionIsAvailable": "Versi terbaru Jellyfin Server bersedia untuk dimuat turunkan.",
|
||||
"NotificationOptionApplicationUpdateAvailable": "Kemas kini aplikasi telah sedia",
|
||||
"NotificationOptionApplicationUpdateInstalled": "Application update installed",
|
||||
"NotificationOptionAudioPlayback": "Audio playback started",
|
||||
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
|
||||
|
||||
@@ -67,5 +67,7 @@
|
||||
"Artists": "นักแสดง",
|
||||
"Application": "แอปพลิเคชั่น",
|
||||
"AppDeviceValues": "App: {0}, อุปกรณ์: {1}",
|
||||
"Albums": "อัลบั้ม"
|
||||
"Albums": "อัลบั้ม",
|
||||
"ScheduledTaskStartedWithName": "{0} เริ่มต้น",
|
||||
"ScheduledTaskFailedWithName": "{0} ล้มเหลว"
|
||||
}
|
||||
|
||||
@@ -152,7 +152,12 @@ namespace Emby.Server.Implementations.Networking
|
||||
return true;
|
||||
}
|
||||
|
||||
byte[] octet = IPAddress.Parse(endpoint).GetAddressBytes();
|
||||
if (!IPAddress.TryParse(endpoint, out var ipAddress))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] octet = ipAddress.GetAddressBytes();
|
||||
|
||||
if ((octet[0] == 10) ||
|
||||
(octet[0] == 172 && (octet[1] >= 16 && octet[1] <= 31)) || // RFC1918
|
||||
@@ -268,6 +273,12 @@ namespace Emby.Server.Implementations.Networking
|
||||
string excludeAddress = "[" + addressString + "]";
|
||||
var subnets = LocalSubnetsFn();
|
||||
|
||||
// Include any address if LAN subnets aren't specified
|
||||
if (subnets.Length == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Exclude any addresses if they appear in the LAN list in [ ]
|
||||
if (Array.IndexOf(subnets, excludeAddress) != -1)
|
||||
{
|
||||
|
||||
@@ -189,5 +189,4 @@ namespace Emby.Server.Implementations.Services
|
||||
return ServiceExecGeneral.Execute(serviceType, req, service, requestDto, requestType.GetMethodName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Emby.Server.Implementations.HttpServer;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Model.Services;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -78,7 +79,8 @@ namespace Emby.Server.Implementations.Services
|
||||
var request = await CreateRequest(httpHost, httpReq, _restPath, logger).ConfigureAwait(false);
|
||||
|
||||
httpHost.ApplyRequestFilters(httpReq, httpRes, request);
|
||||
|
||||
|
||||
httpRes.HttpContext.SetServiceStackRequest(httpReq);
|
||||
var response = await httpHost.ServiceController.Execute(httpHost, request, httpReq).ConfigureAwait(false);
|
||||
|
||||
// Apply response filters
|
||||
|
||||
@@ -488,7 +488,8 @@ namespace Emby.Server.Implementations.Services
|
||||
sb.Append(value);
|
||||
for (var j = pathIx + 1; j < requestComponents.Length; j++)
|
||||
{
|
||||
sb.Append(PathSeperatorChar + requestComponents[j]);
|
||||
sb.Append(PathSeperatorChar)
|
||||
.Append(requestComponents[j]);
|
||||
}
|
||||
|
||||
value = sb.ToString();
|
||||
@@ -505,7 +506,8 @@ namespace Emby.Server.Implementations.Services
|
||||
pathIx++;
|
||||
while (!string.Equals(requestComponents[pathIx], stopLiteral, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
sb.Append(PathSeperatorChar + requestComponents[pathIx++]);
|
||||
sb.Append(PathSeperatorChar)
|
||||
.Append(requestComponents[pathIx++]);
|
||||
}
|
||||
|
||||
value = sb.ToString();
|
||||
|
||||
@@ -117,23 +117,20 @@ namespace Emby.Server.Implementations.TV
|
||||
limit = limit.Value + 10;
|
||||
}
|
||||
|
||||
var items = _libraryManager.GetItemList(new InternalItemsQuery(user)
|
||||
{
|
||||
IncludeItemTypes = new[] { typeof(Episode).Name },
|
||||
OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.DatePlayed, SortOrder.Descending) },
|
||||
SeriesPresentationUniqueKey = presentationUniqueKey,
|
||||
Limit = limit,
|
||||
DtoOptions = new DtoOptions
|
||||
{
|
||||
Fields = new ItemFields[]
|
||||
var items = _libraryManager
|
||||
.GetItemList(
|
||||
new InternalItemsQuery(user)
|
||||
{
|
||||
ItemFields.SeriesPresentationUniqueKey
|
||||
},
|
||||
EnableImages = false
|
||||
},
|
||||
GroupBySeriesPresentationUniqueKey = true
|
||||
|
||||
}, parentsFolders.ToList()).Cast<Episode>().Select(GetUniqueSeriesKey);
|
||||
IncludeItemTypes = new[] { typeof(Episode).Name },
|
||||
OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.DatePlayed, SortOrder.Descending) },
|
||||
SeriesPresentationUniqueKey = presentationUniqueKey,
|
||||
Limit = limit,
|
||||
DtoOptions = new DtoOptions { Fields = new[] { ItemFields.SeriesPresentationUniqueKey }, EnableImages = false },
|
||||
GroupBySeriesPresentationUniqueKey = true
|
||||
}, parentsFolders.ToList())
|
||||
.Cast<Episode>()
|
||||
.Where(episode => !string.IsNullOrEmpty(episode.SeriesPresentationUniqueKey))
|
||||
.Select(GetUniqueSeriesKey);
|
||||
|
||||
// Avoid implicitly captured closure
|
||||
var episodes = GetNextUpEpisodes(request, user, items, dtoOptions);
|
||||
|
||||
@@ -148,6 +148,11 @@ namespace Emby.Server.Implementations.Updates
|
||||
_logger.LogError(ex, "An error occurred while accessing the plugin manifest: {Manifest}", manifest);
|
||||
return Array.Empty<PackageInfo>();
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
_logger.LogError(ex, "An error occurred while accessing the plugin manifest: {Manifest}", manifest);
|
||||
return Array.Empty<PackageInfo>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
Reference in New Issue
Block a user