mirror of https://github.com/jellyfin/jellyfin.git
Merge remote-tracking branch 'upstream/master' into fixes
This commit is contained in:
commit
29d8e38161
|
@ -16,5 +16,11 @@ namespace Emby.Dlna
|
||||||
public string Xml { get; set; }
|
public string Xml { get; set; }
|
||||||
|
|
||||||
public bool IsSuccessful { get; set; }
|
public bool IsSuccessful { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Xml;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -22,7 +24,7 @@ namespace Emby.Server.Implementations.AppBase
|
||||||
{
|
{
|
||||||
object configuration;
|
object configuration;
|
||||||
|
|
||||||
byte[] buffer = null;
|
byte[]? buffer = null;
|
||||||
|
|
||||||
// Use try/catch to avoid the extra file system lookup using File.Exists
|
// Use try/catch to avoid the extra file system lookup using File.Exists
|
||||||
try
|
try
|
||||||
|
@ -36,19 +38,23 @@ namespace Emby.Server.Implementations.AppBase
|
||||||
configuration = Activator.CreateInstance(type);
|
configuration = Activator.CreateInstance(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
using var stream = new MemoryStream();
|
using var stream = new MemoryStream(buffer?.Length ?? 0);
|
||||||
xmlSerializer.SerializeToStream(configuration, stream);
|
xmlSerializer.SerializeToStream(configuration, stream);
|
||||||
|
|
||||||
// Take the object we just got and serialize it back to bytes
|
// Take the object we just got and serialize it back to bytes
|
||||||
var newBytes = stream.ToArray();
|
byte[] newBytes = stream.GetBuffer();
|
||||||
|
int newBytesLen = (int)stream.Length;
|
||||||
|
|
||||||
// If the file didn't exist before, or if something has changed, re-save
|
// If the file didn't exist before, or if something has changed, re-save
|
||||||
if (buffer == null || !buffer.SequenceEqual(newBytes))
|
if (buffer == null || !newBytes.AsSpan(0, newBytesLen).SequenceEqual(buffer))
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||||
|
|
||||||
// Save it after load in case we got new items
|
// Save it after load in case we got new items
|
||||||
File.WriteAllBytes(path, newBytes);
|
using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read))
|
||||||
|
{
|
||||||
|
fs.Write(newBytes, 0, newBytesLen);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return configuration;
|
return configuration;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Emby.Server.Implementations.HttpServer;
|
using Emby.Server.Implementations.HttpServer;
|
||||||
using Emby.Server.Implementations.Updates;
|
|
||||||
using static MediaBrowser.Controller.Extensions.ConfigurationExtensions;
|
using static MediaBrowser.Controller.Extensions.ConfigurationExtensions;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations
|
namespace Emby.Server.Implementations
|
||||||
|
@ -19,7 +18,8 @@ namespace Emby.Server.Implementations
|
||||||
{ HttpListenerHost.DefaultRedirectKey, "web/index.html" },
|
{ HttpListenerHost.DefaultRedirectKey, "web/index.html" },
|
||||||
{ FfmpegProbeSizeKey, "1G" },
|
{ FfmpegProbeSizeKey, "1G" },
|
||||||
{ FfmpegAnalyzeDurationKey, "200M" },
|
{ FfmpegAnalyzeDurationKey, "200M" },
|
||||||
{ PlaylistsAllowDuplicatesKey, bool.TrueString }
|
{ PlaylistsAllowDuplicatesKey, bool.TrueString },
|
||||||
|
{ BindToUnixSocketKey, bool.FalseString }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,25 +73,6 @@ namespace Emby.Server.Implementations.Dto
|
||||||
_livetvManagerFactory = livetvManagerFactory;
|
_livetvManagerFactory = livetvManagerFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts a BaseItem to a DTOBaseItem.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="item">The item.</param>
|
|
||||||
/// <param name="fields">The fields.</param>
|
|
||||||
/// <param name="user">The user.</param>
|
|
||||||
/// <param name="owner">The owner.</param>
|
|
||||||
/// <returns>Task{DtoBaseItem}.</returns>
|
|
||||||
/// <exception cref="ArgumentNullException">item</exception>
|
|
||||||
public BaseItemDto GetBaseItemDto(BaseItem item, ItemFields[] fields, User user = null, BaseItem owner = null)
|
|
||||||
{
|
|
||||||
var options = new DtoOptions
|
|
||||||
{
|
|
||||||
Fields = fields
|
|
||||||
};
|
|
||||||
|
|
||||||
return GetBaseItemDto(item, options, user, owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IReadOnlyList<BaseItemDto> GetBaseItemDtos(IReadOnlyList<BaseItem> items, DtoOptions options, User user = null, BaseItem owner = null)
|
public IReadOnlyList<BaseItemDto> GetBaseItemDtos(IReadOnlyList<BaseItem> items, DtoOptions options, User user = null, BaseItem owner = null)
|
||||||
{
|
{
|
||||||
|
@ -443,17 +424,6 @@ namespace Emby.Server.Implementations.Dto
|
||||||
return folder.GetChildCount(user);
|
return folder.GetChildCount(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets client-side Id of a server-side BaseItem.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="item">The item.</param>
|
|
||||||
/// <returns>System.String.</returns>
|
|
||||||
/// <exception cref="ArgumentNullException">item</exception>
|
|
||||||
public string GetDtoId(BaseItem item)
|
|
||||||
{
|
|
||||||
return item.Id.ToString("N", CultureInfo.InvariantCulture);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SetBookProperties(BaseItemDto dto, Book item)
|
private static void SetBookProperties(BaseItemDto dto, Book item)
|
||||||
{
|
{
|
||||||
dto.SeriesName = item.SeriesName;
|
dto.SeriesName = item.SeriesName;
|
||||||
|
@ -484,6 +454,11 @@ namespace Emby.Server.Implementations.Dto
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string GetDtoId(BaseItem item)
|
||||||
|
{
|
||||||
|
return item.Id.ToString("N", CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
private void SetMusicVideoProperties(BaseItemDto dto, MusicVideo item)
|
private void SetMusicVideoProperties(BaseItemDto dto, MusicVideo item)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(item.Album))
|
if (!string.IsNullOrEmpty(item.Album))
|
||||||
|
@ -513,19 +488,6 @@ namespace Emby.Server.Implementations.Dto
|
||||||
.ToArray();
|
.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetImageCacheTag(BaseItem item, ImageType type)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return _imageProcessor.GetImageCacheTag(item, type);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error getting {type} image info", type);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetImageCacheTag(BaseItem item, ItemImageInfo image)
|
private string GetImageCacheTag(BaseItem item, ItemImageInfo image)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
|
@ -105,7 +105,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
responseHeaders = new Dictionary<string, string>();
|
responseHeaders = new Dictionary<string, string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addCachePrevention && !responseHeaders.TryGetValue(HeaderNames.Expires, out string expires))
|
if (addCachePrevention && !responseHeaders.TryGetValue(HeaderNames.Expires, out _))
|
||||||
{
|
{
|
||||||
responseHeaders[HeaderNames.Expires] = "0";
|
responseHeaders[HeaderNames.Expires] = "0";
|
||||||
}
|
}
|
||||||
|
@ -326,7 +326,8 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
return GetHttpResult(request, ms, contentType, true, responseHeaders);
|
return GetHttpResult(request, ms, contentType, true, responseHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IHasHeaders GetCompressedResult(byte[] content,
|
private IHasHeaders GetCompressedResult(
|
||||||
|
byte[] content,
|
||||||
string requestedCompressionType,
|
string requestedCompressionType,
|
||||||
IDictionary<string, string> responseHeaders,
|
IDictionary<string, string> responseHeaders,
|
||||||
bool isHeadRequest,
|
bool isHeadRequest,
|
||||||
|
|
|
@ -95,13 +95,13 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
|
|
||||||
if (bytes != null)
|
if (bytes != null)
|
||||||
{
|
{
|
||||||
await responseStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
|
await responseStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
using (var src = SourceStream)
|
using (var src = SourceStream)
|
||||||
{
|
{
|
||||||
await src.CopyToAsync(responseStream).ConfigureAwait(false);
|
await src.CopyToAsync(responseStream, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,7 @@ namespace Emby.Server.Implementations.LiveTv
|
||||||
public class LiveTvMediaSourceProvider : IMediaSourceProvider
|
public class LiveTvMediaSourceProvider : IMediaSourceProvider
|
||||||
{
|
{
|
||||||
// Do not use a pipe here because Roku http requests to the server will fail, without any explicit error message.
|
// Do not use a pipe here because Roku http requests to the server will fail, without any explicit error message.
|
||||||
private const char StreamIdDelimeter = '_';
|
private const char StreamIdDelimiter = '_';
|
||||||
private const string StreamIdDelimeterString = "_";
|
|
||||||
|
|
||||||
private readonly ILiveTvManager _liveTvManager;
|
private readonly ILiveTvManager _liveTvManager;
|
||||||
private readonly ILogger<LiveTvMediaSourceProvider> _logger;
|
private readonly ILogger<LiveTvMediaSourceProvider> _logger;
|
||||||
|
@ -47,7 +46,7 @@ namespace Emby.Server.Implementations.LiveTv
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.FromResult<IEnumerable<MediaSourceInfo>>(Array.Empty<MediaSourceInfo>());
|
return Task.FromResult(Enumerable.Empty<MediaSourceInfo>());
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IEnumerable<MediaSourceInfo>> GetMediaSourcesInternal(BaseItem item, ActiveRecordingInfo activeRecordingInfo, CancellationToken cancellationToken)
|
private async Task<IEnumerable<MediaSourceInfo>> GetMediaSourcesInternal(BaseItem item, ActiveRecordingInfo activeRecordingInfo, CancellationToken cancellationToken)
|
||||||
|
@ -98,7 +97,7 @@ namespace Emby.Server.Implementations.LiveTv
|
||||||
source.Id ?? string.Empty
|
source.Id ?? string.Empty
|
||||||
};
|
};
|
||||||
|
|
||||||
source.OpenToken = string.Join(StreamIdDelimeterString, openKeys);
|
source.OpenToken = string.Join(StreamIdDelimiter, openKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dummy this up so that direct play checks can still run
|
// Dummy this up so that direct play checks can still run
|
||||||
|
@ -116,7 +115,7 @@ namespace Emby.Server.Implementations.LiveTv
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<ILiveStream> OpenMediaSource(string openToken, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken)
|
public async Task<ILiveStream> OpenMediaSource(string openToken, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var keys = openToken.Split(new[] { StreamIdDelimeter }, 3);
|
var keys = openToken.Split(StreamIdDelimiter, 3);
|
||||||
var mediaSourceId = keys.Length >= 3 ? keys[2] : null;
|
var mediaSourceId = keys.Length >= 3 ? keys[2] : null;
|
||||||
|
|
||||||
var info = await _liveTvManager.GetChannelStream(keys[1], mediaSourceId, currentLiveStreams, cancellationToken).ConfigureAwait(false);
|
var info = await _liveTvManager.GetChannelStream(keys[1], mediaSourceId, currentLiveStreams, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
|
@ -14,7 +14,7 @@ using MediaBrowser.Controller.LiveTv;
|
||||||
using MediaBrowser.Model.Dto;
|
using MediaBrowser.Model.Dto;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.LiveTv;
|
using MediaBrowser.Model.LiveTv;
|
||||||
using MediaBrowser.Model.Serialization;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||||
|
@ -23,17 +23,15 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||||
{
|
{
|
||||||
protected readonly IServerConfigurationManager Config;
|
protected readonly IServerConfigurationManager Config;
|
||||||
protected readonly ILogger<BaseTunerHost> Logger;
|
protected readonly ILogger<BaseTunerHost> Logger;
|
||||||
protected IJsonSerializer JsonSerializer;
|
|
||||||
protected readonly IFileSystem FileSystem;
|
protected readonly IFileSystem FileSystem;
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<string, ChannelCache> _channelCache =
|
private readonly IMemoryCache _memoryCache;
|
||||||
new ConcurrentDictionary<string, ChannelCache>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
protected BaseTunerHost(IServerConfigurationManager config, ILogger<BaseTunerHost> logger, IJsonSerializer jsonSerializer, IFileSystem fileSystem)
|
protected BaseTunerHost(IServerConfigurationManager config, ILogger<BaseTunerHost> logger, IFileSystem fileSystem, IMemoryCache memoryCache)
|
||||||
{
|
{
|
||||||
Config = config;
|
Config = config;
|
||||||
Logger = logger;
|
Logger = logger;
|
||||||
JsonSerializer = jsonSerializer;
|
_memoryCache = memoryCache;
|
||||||
FileSystem = fileSystem;
|
FileSystem = fileSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,23 +42,19 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||||
|
|
||||||
public async Task<List<ChannelInfo>> GetChannels(TunerHostInfo tuner, bool enableCache, CancellationToken cancellationToken)
|
public async Task<List<ChannelInfo>> GetChannels(TunerHostInfo tuner, bool enableCache, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
ChannelCache cache = null;
|
|
||||||
var key = tuner.Id;
|
var key = tuner.Id;
|
||||||
|
|
||||||
if (enableCache && !string.IsNullOrEmpty(key) && _channelCache.TryGetValue(key, out cache))
|
if (enableCache && !string.IsNullOrEmpty(key) && _memoryCache.TryGetValue(key, out List<ChannelInfo> cache))
|
||||||
{
|
{
|
||||||
return cache.Channels.ToList();
|
return cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = await GetChannelsInternal(tuner, cancellationToken).ConfigureAwait(false);
|
var list = await GetChannelsInternal(tuner, cancellationToken).ConfigureAwait(false);
|
||||||
var list = result.ToList();
|
|
||||||
// logger.LogInformation("Channels from {0}: {1}", tuner.Url, JsonSerializer.SerializeToString(list));
|
// logger.LogInformation("Channels from {0}: {1}", tuner.Url, JsonSerializer.SerializeToString(list));
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(key) && list.Count > 0)
|
if (!string.IsNullOrEmpty(key) && list.Count > 0)
|
||||||
{
|
{
|
||||||
cache = cache ?? new ChannelCache();
|
_memoryCache.Set(key, list);
|
||||||
cache.Channels = list;
|
|
||||||
_channelCache.AddOrUpdate(key, cache, (k, v) => cache);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
|
@ -95,7 +89,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(channelCacheFile));
|
Directory.CreateDirectory(Path.GetDirectoryName(channelCacheFile));
|
||||||
JsonSerializer.SerializeToFile(channels, channelCacheFile);
|
await using var writeStream = File.OpenWrite(channelCacheFile);
|
||||||
|
await JsonSerializer.SerializeAsync(writeStream, channels, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (IOException)
|
catch (IOException)
|
||||||
{
|
{
|
||||||
|
@ -110,7 +105,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var channels = JsonSerializer.DeserializeFromFile<List<ChannelInfo>>(channelCacheFile);
|
await using var readStream = File.OpenRead(channelCacheFile);
|
||||||
|
var channels = await JsonSerializer.DeserializeAsync<List<ChannelInfo>>(readStream, cancellationToken: cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
list.AddRange(channels);
|
list.AddRange(channels);
|
||||||
}
|
}
|
||||||
catch (IOException)
|
catch (IOException)
|
||||||
|
@ -233,10 +230,5 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||||
{
|
{
|
||||||
return Config.GetConfiguration<LiveTvOptions>("livetv");
|
return Config.GetConfiguration<LiveTvOptions>("livetv");
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ChannelCache
|
|
||||||
{
|
|
||||||
public List<ChannelInfo> Channels;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
|
@ -23,7 +24,7 @@ using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.LiveTv;
|
using MediaBrowser.Model.LiveTv;
|
||||||
using MediaBrowser.Model.MediaInfo;
|
using MediaBrowser.Model.MediaInfo;
|
||||||
using MediaBrowser.Model.Net;
|
using MediaBrowser.Model.Net;
|
||||||
using MediaBrowser.Model.Serialization;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
|
@ -39,14 +40,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
public HdHomerunHost(
|
public HdHomerunHost(
|
||||||
IServerConfigurationManager config,
|
IServerConfigurationManager config,
|
||||||
ILogger<HdHomerunHost> logger,
|
ILogger<HdHomerunHost> logger,
|
||||||
IJsonSerializer jsonSerializer,
|
|
||||||
IFileSystem fileSystem,
|
IFileSystem fileSystem,
|
||||||
IHttpClient httpClient,
|
IHttpClient httpClient,
|
||||||
IServerApplicationHost appHost,
|
IServerApplicationHost appHost,
|
||||||
ISocketFactory socketFactory,
|
ISocketFactory socketFactory,
|
||||||
INetworkManager networkManager,
|
INetworkManager networkManager,
|
||||||
IStreamHelper streamHelper)
|
IStreamHelper streamHelper,
|
||||||
: base(config, logger, jsonSerializer, fileSystem)
|
IMemoryCache memoryCache)
|
||||||
|
: base(config, logger, fileSystem, memoryCache)
|
||||||
{
|
{
|
||||||
_httpClient = httpClient;
|
_httpClient = httpClient;
|
||||||
_appHost = appHost;
|
_appHost = appHost;
|
||||||
|
@ -75,18 +76,17 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
BufferContent = false
|
BufferContent = false
|
||||||
};
|
};
|
||||||
|
|
||||||
using (var response = await _httpClient.SendAsync(options, HttpMethod.Get).ConfigureAwait(false))
|
using var response = await _httpClient.SendAsync(options, HttpMethod.Get).ConfigureAwait(false);
|
||||||
using (var stream = response.Content)
|
await using var stream = response.Content;
|
||||||
|
var lineup = await JsonSerializer.DeserializeAsync<List<Channels>>(stream, cancellationToken: cancellationToken)
|
||||||
|
.ConfigureAwait(false) ?? new List<Channels>();
|
||||||
|
|
||||||
|
if (info.ImportFavoritesOnly)
|
||||||
{
|
{
|
||||||
var lineup = await JsonSerializer.DeserializeFromStreamAsync<List<Channels>>(stream).ConfigureAwait(false) ?? new List<Channels>();
|
lineup = lineup.Where(i => i.Favorite).ToList();
|
||||||
|
|
||||||
if (info.ImportFavoritesOnly)
|
|
||||||
{
|
|
||||||
lineup = lineup.Where(i => i.Favorite).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
return lineup.Where(i => !i.DRM).ToList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return lineup.Where(i => !i.DRM).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class HdHomerunChannelInfo : ChannelInfo
|
private class HdHomerunChannelInfo : ChannelInfo
|
||||||
|
@ -132,30 +132,30 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (var response = await _httpClient.SendAsync(new HttpRequestOptions()
|
using var response = await _httpClient.SendAsync(
|
||||||
|
new HttpRequestOptions
|
||||||
{
|
{
|
||||||
Url = string.Format("{0}/discover.json", GetApiUrl(info)),
|
Url = string.Format(CultureInfo.InvariantCulture, "{0}/discover.json", GetApiUrl(info)),
|
||||||
CancellationToken = cancellationToken,
|
CancellationToken = cancellationToken,
|
||||||
BufferContent = false
|
BufferContent = false
|
||||||
}, HttpMethod.Get).ConfigureAwait(false))
|
}, HttpMethod.Get).ConfigureAwait(false);
|
||||||
using (var stream = response.Content)
|
await using var stream = response.Content;
|
||||||
|
var discoverResponse = await JsonSerializer.DeserializeAsync<DiscoverResponse>(stream, cancellationToken: cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(cacheKey))
|
||||||
{
|
{
|
||||||
var discoverResponse = await JsonSerializer.DeserializeFromStreamAsync<DiscoverResponse>(stream).ConfigureAwait(false);
|
lock (_modelCache)
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(cacheKey))
|
|
||||||
{
|
{
|
||||||
lock (_modelCache)
|
_modelCache[cacheKey] = discoverResponse;
|
||||||
{
|
|
||||||
_modelCache[cacheKey] = discoverResponse;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return discoverResponse;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return discoverResponse;
|
||||||
}
|
}
|
||||||
catch (HttpException ex)
|
catch (HttpException ex)
|
||||||
{
|
{
|
||||||
if (!throwAllExceptions && ex.StatusCode.HasValue && ex.StatusCode.Value == System.Net.HttpStatusCode.NotFound)
|
if (!throwAllExceptions && ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound)
|
||||||
{
|
{
|
||||||
var defaultValue = "HDHR";
|
var defaultValue = "HDHR";
|
||||||
var response = new DiscoverResponse
|
var response = new DiscoverResponse
|
||||||
|
|
|
@ -18,7 +18,7 @@ using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.LiveTv;
|
using MediaBrowser.Model.LiveTv;
|
||||||
using MediaBrowser.Model.MediaInfo;
|
using MediaBrowser.Model.MediaInfo;
|
||||||
using MediaBrowser.Model.Serialization;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
|
@ -36,13 +36,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||||
IServerConfigurationManager config,
|
IServerConfigurationManager config,
|
||||||
IMediaSourceManager mediaSourceManager,
|
IMediaSourceManager mediaSourceManager,
|
||||||
ILogger<M3UTunerHost> logger,
|
ILogger<M3UTunerHost> logger,
|
||||||
IJsonSerializer jsonSerializer,
|
|
||||||
IFileSystem fileSystem,
|
IFileSystem fileSystem,
|
||||||
IHttpClient httpClient,
|
IHttpClient httpClient,
|
||||||
IServerApplicationHost appHost,
|
IServerApplicationHost appHost,
|
||||||
INetworkManager networkManager,
|
INetworkManager networkManager,
|
||||||
IStreamHelper streamHelper)
|
IStreamHelper streamHelper,
|
||||||
: base(config, logger, jsonSerializer, fileSystem)
|
IMemoryCache memoryCache)
|
||||||
|
: base(config, logger, fileSystem, memoryCache)
|
||||||
{
|
{
|
||||||
_httpClient = httpClient;
|
_httpClient = httpClient;
|
||||||
_appHost = appHost;
|
_appHost = appHost;
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"DeviceOnlineWithName": "{0}-এর সাথে সংযুক্ত হয়েছে",
|
"DeviceOnlineWithName": "{0}-এর সাথে সংযুক্ত হয়েছে",
|
||||||
"DeviceOfflineWithName": "{0}-এর সাথে সংযোগ বিচ্ছিন্ন হয়েছে",
|
"DeviceOfflineWithName": "{0}-এর সাথে সংযোগ বিচ্ছিন্ন হয়েছে",
|
||||||
"Collections": "সংকলন",
|
"Collections": "কলেক্শন",
|
||||||
"ChapterNameValue": "অধ্যায় {0}",
|
"ChapterNameValue": "অধ্যায় {0}",
|
||||||
"Channels": "চ্যানেল",
|
"Channels": "চ্যানেল",
|
||||||
"CameraImageUploadedFrom": "একটি নতুন ক্যামেরার চিত্র আপলোড করা হয়েছে {0} থেকে",
|
"CameraImageUploadedFrom": "{0} থেকে একটি নতুন ক্যামেরার চিত্র আপলোড করা হয়েছে",
|
||||||
"Books": "বই",
|
"Books": "বই",
|
||||||
"AuthenticationSucceededWithUserName": "{0} যাচাই সফল",
|
"AuthenticationSucceededWithUserName": "{0} অনুমোদন সফল",
|
||||||
"Artists": "শিল্পীরা",
|
"Artists": "শিল্পীরা",
|
||||||
"Application": "অ্যাপ্লিকেশন",
|
"Application": "অ্যাপ্লিকেশন",
|
||||||
"Albums": "অ্যালবামগুলো",
|
"Albums": "অ্যালবামগুলো",
|
||||||
|
@ -14,13 +14,13 @@
|
||||||
"HeaderFavoriteArtists": "প্রিয় শিল্পীরা",
|
"HeaderFavoriteArtists": "প্রিয় শিল্পীরা",
|
||||||
"HeaderFavoriteAlbums": "প্রিয় এলবামগুলো",
|
"HeaderFavoriteAlbums": "প্রিয় এলবামগুলো",
|
||||||
"HeaderContinueWatching": "দেখতে থাকুন",
|
"HeaderContinueWatching": "দেখতে থাকুন",
|
||||||
"HeaderCameraUploads": "ক্যামেরার আপলোডগুলো",
|
"HeaderCameraUploads": "ক্যামেরার আপলোড সমূহ",
|
||||||
"HeaderAlbumArtists": "এলবামের শিল্পী",
|
"HeaderAlbumArtists": "এলবাম শিল্পী",
|
||||||
"Genres": "ঘরানা",
|
"Genres": "জেনার",
|
||||||
"Folders": "ফোল্ডারগুলো",
|
"Folders": "ফোল্ডারগুলো",
|
||||||
"Favorites": "ফেভারিটগুলো",
|
"Favorites": "পছন্দসমূহ",
|
||||||
"FailedLoginAttemptWithUserName": "{0} লগিন করতে ব্যর্থ হয়েছে",
|
"FailedLoginAttemptWithUserName": "{0} লগিন করতে ব্যর্থ হয়েছে",
|
||||||
"AppDeviceValues": "এপ: {0}, ডিভাইস: {0}",
|
"AppDeviceValues": "অ্যাপ: {0}, ডিভাইস: {0}",
|
||||||
"VersionNumber": "সংস্করণ {0}",
|
"VersionNumber": "সংস্করণ {0}",
|
||||||
"ValueSpecialEpisodeName": "বিশেষ - {0}",
|
"ValueSpecialEpisodeName": "বিশেষ - {0}",
|
||||||
"ValueHasBeenAddedToLibrary": "আপনার লাইব্রেরিতে {0} যোগ করা হয়েছে",
|
"ValueHasBeenAddedToLibrary": "আপনার লাইব্রেরিতে {0} যোগ করা হয়েছে",
|
||||||
|
@ -74,20 +74,20 @@
|
||||||
"NameInstallFailed": "{0} ইন্সটল ব্যর্থ",
|
"NameInstallFailed": "{0} ইন্সটল ব্যর্থ",
|
||||||
"MusicVideos": "গানের ভিডিও",
|
"MusicVideos": "গানের ভিডিও",
|
||||||
"Music": "গান",
|
"Music": "গান",
|
||||||
"Movies": "সিনেমা",
|
"Movies": "চলচ্চিত্র",
|
||||||
"MixedContent": "মিশ্র কন্টেন্ট",
|
"MixedContent": "মিশ্র কন্টেন্ট",
|
||||||
"MessageServerConfigurationUpdated": "সার্ভারের কনফিগারেশন হালনাগাদ করা হয়েছে",
|
"MessageServerConfigurationUpdated": "সার্ভারের কনফিগারেশন আপডেট করা হয়েছে",
|
||||||
"HeaderRecordingGroups": "রেকর্ডিং গ্রুপ",
|
"HeaderRecordingGroups": "রেকর্ডিং দল",
|
||||||
"MessageNamedServerConfigurationUpdatedWithValue": "সার্ভারের {0} কনফিগারেসন অংশ আপডেট করা হয়েছে",
|
"MessageNamedServerConfigurationUpdatedWithValue": "সার্ভারের {0} কনফিগারেসনের অংশ আপডেট করা হয়েছে",
|
||||||
"MessageApplicationUpdatedTo": "জেলিফিন সার্ভার {0} তে হালনাগাদ করা হয়েছে",
|
"MessageApplicationUpdatedTo": "জেলিফিন সার্ভার {0} তে আপডেট করা হয়েছে",
|
||||||
"MessageApplicationUpdated": "জেলিফিন সার্ভার হালনাগাদ করা হয়েছে",
|
"MessageApplicationUpdated": "জেলিফিন সার্ভার আপডেট করা হয়েছে",
|
||||||
"Latest": "একদম নতুন",
|
"Latest": "সর্বশেষ",
|
||||||
"LabelRunningTimeValue": "চলার সময়: {0}",
|
"LabelRunningTimeValue": "চলার সময়: {0}",
|
||||||
"LabelIpAddressValue": "আইপি ঠিকানা: {0}",
|
"LabelIpAddressValue": "আইপি এড্রেস: {0}",
|
||||||
"ItemRemovedWithName": "{0} লাইব্রেরি থেকে বাদ দেয়া হয়েছে",
|
"ItemRemovedWithName": "{0} লাইব্রেরি থেকে বাদ দেয়া হয়েছে",
|
||||||
"ItemAddedWithName": "{0} লাইব্রেরিতে যোগ করা হয়েছে",
|
"ItemAddedWithName": "{0} লাইব্রেরিতে যোগ করা হয়েছে",
|
||||||
"Inherit": "থেকে পাওয়া",
|
"Inherit": "থেকে পাওয়া",
|
||||||
"HomeVideos": "বাসার ভিডিও",
|
"HomeVideos": "হোম ভিডিও",
|
||||||
"HeaderNextUp": "এরপরে আসছে",
|
"HeaderNextUp": "এরপরে আসছে",
|
||||||
"HeaderLiveTV": "লাইভ টিভি",
|
"HeaderLiveTV": "লাইভ টিভি",
|
||||||
"HeaderFavoriteSongs": "প্রিয় গানগুলো",
|
"HeaderFavoriteSongs": "প্রিয় গানগুলো",
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
"TvShows": "தொலைக்காட்சித் தொடர்கள்",
|
"TvShows": "தொலைக்காட்சித் தொடர்கள்",
|
||||||
"Sync": "ஒத்திசைவு",
|
"Sync": "ஒத்திசைவு",
|
||||||
"StartupEmbyServerIsLoading": "ஜெல்லிஃபின் சேவையகம் துவங்குகிறது. சிறிது நேரம் கழித்து முயற்சிக்கவும்.",
|
"StartupEmbyServerIsLoading": "ஜெல்லிஃபின் சேவையகம் துவங்குகிறது. சிறிது நேரம் கழித்து முயற்சிக்கவும்.",
|
||||||
"Songs": "பாட்டுகள்",
|
"Songs": "பாடல்கள்",
|
||||||
"Shows": "தொடர்கள்",
|
"Shows": "தொடர்கள்",
|
||||||
"ServerNameNeedsToBeRestarted": "{0} மறுதொடக்கம் செய்யப்பட வேண்டும்",
|
"ServerNameNeedsToBeRestarted": "{0} மறுதொடக்கம் செய்யப்பட வேண்டும்",
|
||||||
"ScheduledTaskStartedWithName": "{0} துவங்கியது",
|
"ScheduledTaskStartedWithName": "{0} துவங்கியது",
|
||||||
|
@ -93,7 +93,25 @@
|
||||||
"Channels": "சேனல்கள்",
|
"Channels": "சேனல்கள்",
|
||||||
"Books": "புத்தகங்கள்",
|
"Books": "புத்தகங்கள்",
|
||||||
"AuthenticationSucceededWithUserName": "{0} வெற்றிகரமாக அங்கீகரிக்கப்பட்டது",
|
"AuthenticationSucceededWithUserName": "{0} வெற்றிகரமாக அங்கீகரிக்கப்பட்டது",
|
||||||
"Artists": "கலைஞர்கள்",
|
"Artists": "கலைஞர்",
|
||||||
"Application": "செயலி",
|
"Application": "செயலி",
|
||||||
"Albums": "ஆல்பங்கள்"
|
"Albums": "ஆல்பங்கள்",
|
||||||
|
"NewVersionIsAvailable": "ஜெல்லிஃபின் சேவையகத்தின் புதிய பதிப்பு பதிவிறக்கத்திற்கு கிடைக்கிறது.",
|
||||||
|
"MessageNamedServerConfigurationUpdatedWithValue": "சேவையக உள்ளமைவு பிரிவு {0 புதுப்பிக்கப்பட்டது",
|
||||||
|
"TaskCleanCacheDescription": "கணினிக்கு இனி தேவைப்படாத தற்காலிக கோப்புகளை நீக்கு.",
|
||||||
|
"UserOfflineFromDevice": "{0} இலிருந்து {1} துண்டிக்கப்பட்டுள்ளது",
|
||||||
|
"SubtitleDownloadFailureFromForItem": "வசன வரிகள் {0 } இலிருந்து {1} க்கு பதிவிறக்கத் தவறிவிட்டன",
|
||||||
|
"TaskDownloadMissingSubtitlesDescription": "மெட்டாடேட்டா உள்ளமைவின் அடிப்படையில் வசன வரிகள் காணாமல் போனதற்கு இணையத்தைத் தேடுகிறது.",
|
||||||
|
"TaskCleanTranscodeDescription": "டிரான்ஸ்கோட் கோப்புகளை ஒரு நாளுக்கு மேல் பழையதாக நீக்குகிறது.",
|
||||||
|
"TaskUpdatePluginsDescription": "தானாகவே புதுப்பிக்க கட்டமைக்கப்பட்ட செருகுநிரல்களுக்கான புதுப்பிப்புகளை பதிவிறக்குகிறது மற்றும் நிறுவுகிறது.",
|
||||||
|
"TaskRefreshPeopleDescription": "உங்கள் மீடியா நூலகத்தில் உள்ள நடிகர்கள் மற்றும் இயக்குனர்களுக்கான மெட்டாடேட்டாவை புதுப்பிக்கும்.",
|
||||||
|
"TaskCleanLogsDescription": "{0} நாட்களுக்கு மேல் இருக்கும் பதிவு கோப்புகளை நீக்கும்.",
|
||||||
|
"TaskCleanLogs": "பதிவு அடைவு சுத்தம் செய்யுங்கள்",
|
||||||
|
"TaskRefreshLibraryDescription": "புதிய கோப்புகளுக்காக உங்கள் மீடியா நூலகத்தை ஸ்கேன் செய்து மீத்தரவை புதுப்பிக்கும்.",
|
||||||
|
"TaskRefreshChapterImagesDescription": "அத்தியாயங்களைக் கொண்ட வீடியோக்களுக்கான சிறு உருவங்களை உருவாக்குகிறது.",
|
||||||
|
"ValueHasBeenAddedToLibrary": "உங்கள் மீடியா நூலகத்தில் {0} சேர்க்கப்பட்டது",
|
||||||
|
"UserOnlineFromDevice": "{1} இருந்து {0} ஆன்லைன்",
|
||||||
|
"HomeVideos": "முகப்பு வீடியோக்கள்",
|
||||||
|
"UserStoppedPlayingItemWithValues": "{2} இல் {1} முடித்துவிட்டது",
|
||||||
|
"UserStartedPlayingItemWithValues": "{0} {2}இல் {1} ஐ இயக்குகிறது"
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ using System.Collections.Concurrent;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using System.Xml.Serialization;
|
using System.Xml.Serialization;
|
||||||
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.Serialization;
|
using MediaBrowser.Model.Serialization;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Serialization
|
namespace Emby.Server.Implementations.Serialization
|
||||||
|
@ -53,10 +54,11 @@ namespace Emby.Server.Implementations.Serialization
|
||||||
/// <param name="stream">The stream.</param>
|
/// <param name="stream">The stream.</param>
|
||||||
public void SerializeToStream(object obj, Stream stream)
|
public void SerializeToStream(object obj, Stream stream)
|
||||||
{
|
{
|
||||||
using (var writer = new XmlTextWriter(stream, null))
|
using (var writer = new StreamWriter(stream, null, IODefaults.StreamWriterBufferSize, true))
|
||||||
|
using (var textWriter = new XmlTextWriter(writer))
|
||||||
{
|
{
|
||||||
writer.Formatting = Formatting.Indented;
|
textWriter.Formatting = Formatting.Indented;
|
||||||
SerializeToWriter(obj, writer);
|
SerializeToWriter(obj, textWriter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +97,7 @@ namespace Emby.Server.Implementations.Serialization
|
||||||
/// <returns>System.Object.</returns>
|
/// <returns>System.Object.</returns>
|
||||||
public object DeserializeFromBytes(Type type, byte[] buffer)
|
public object DeserializeFromBytes(Type type, byte[] buffer)
|
||||||
{
|
{
|
||||||
using (var stream = new MemoryStream(buffer))
|
using (var stream = new MemoryStream(buffer, 0, buffer.Length, false, true))
|
||||||
{
|
{
|
||||||
return DeserializeFromStream(type, stream);
|
return DeserializeFromStream(type, stream);
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,8 +60,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <param name="serverId">Server UUID.</param>
|
/// <param name="serverId">Server UUID.</param>
|
||||||
/// <response code="200">Dlna content directory returned.</response>
|
/// <response code="200">Dlna content directory returned.</response>
|
||||||
/// <returns>An <see cref="OkResult"/> containing the dlna content directory xml.</returns>
|
/// <returns>An <see cref="OkResult"/> containing the dlna content directory xml.</returns>
|
||||||
[HttpGet("{serverId}/ContentDirectory/ContentDirectory")]
|
[HttpGet("{serverId}/ContentDirectory")]
|
||||||
[HttpGet("{serverId}/ContentDirectory/ContentDirectory.xml", Name = "GetContentDirectory_2")]
|
[HttpGet("{serverId}/ContentDirectory.xml", Name = "GetContentDirectory_2")]
|
||||||
[Produces(XMLContentType)]
|
[Produces(XMLContentType)]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
|
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
|
||||||
|
@ -75,8 +75,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="serverId">Server UUID.</param>
|
/// <param name="serverId">Server UUID.</param>
|
||||||
/// <returns>Dlna media receiver registrar xml.</returns>
|
/// <returns>Dlna media receiver registrar xml.</returns>
|
||||||
[HttpGet("{serverId}/MediaReceiverRegistrar/MediaReceiverRegistrar")]
|
[HttpGet("{serverId}/MediaReceiverRegistrar")]
|
||||||
[HttpGet("{serverId}/MediaReceiverRegistrar/MediaReceiverRegistrar.xml", Name = "GetMediaReceiverRegistrar_2")]
|
[HttpGet("{serverId}/MediaReceiverRegistrar.xml", Name = "GetMediaReceiverRegistrar_2")]
|
||||||
[Produces(XMLContentType)]
|
[Produces(XMLContentType)]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
|
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
|
||||||
|
@ -90,8 +90,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="serverId">Server UUID.</param>
|
/// <param name="serverId">Server UUID.</param>
|
||||||
/// <returns>Dlna media receiver registrar xml.</returns>
|
/// <returns>Dlna media receiver registrar xml.</returns>
|
||||||
[HttpGet("{serverId}/ConnectionManager/ConnectionManager")]
|
[HttpGet("{serverId}/ConnectionManager")]
|
||||||
[HttpGet("{serverId}/ConnectionManager/ConnectionManager.xml", Name = "GetConnectionManager_2")]
|
[HttpGet("{serverId}/ConnectionManager.xml", Name = "GetConnectionManager_2")]
|
||||||
[Produces(XMLContentType)]
|
[Produces(XMLContentType)]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
|
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
|
||||||
|
|
|
@ -969,7 +969,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
var text = await reader.ReadToEndAsync().ConfigureAwait(false);
|
var text = await reader.ReadToEndAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
var bytes = Convert.FromBase64String(text);
|
var bytes = Convert.FromBase64String(text);
|
||||||
return new MemoryStream(bytes) { Position = 0 };
|
return new MemoryStream(bytes, 0, bytes.Length, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImageInfo? GetImageInfo(BaseItem item, ItemImageInfo info, int? imageIndex)
|
private ImageInfo? GetImageInfo(BaseItem item, ItemImageInfo info, int? imageIndex)
|
||||||
|
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
using System.Net.Mime;
|
using System.Net.Mime;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
@ -15,7 +16,6 @@ using Jellyfin.Api.Models.LiveTvDtos;
|
||||||
using Jellyfin.Data.Enums;
|
using Jellyfin.Data.Enums;
|
||||||
using MediaBrowser.Common;
|
using MediaBrowser.Common;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
using MediaBrowser.Common.Net;
|
|
||||||
using MediaBrowser.Controller.Dto;
|
using MediaBrowser.Controller.Dto;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.TV;
|
using MediaBrowser.Controller.Entities.TV;
|
||||||
|
@ -39,7 +39,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
{
|
{
|
||||||
private readonly ILiveTvManager _liveTvManager;
|
private readonly ILiveTvManager _liveTvManager;
|
||||||
private readonly IUserManager _userManager;
|
private readonly IUserManager _userManager;
|
||||||
private readonly IHttpClient _httpClient;
|
private readonly IHttpClientFactory _httpClientFactory;
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
private readonly IDtoService _dtoService;
|
private readonly IDtoService _dtoService;
|
||||||
private readonly ISessionContext _sessionContext;
|
private readonly ISessionContext _sessionContext;
|
||||||
|
@ -52,7 +52,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="liveTvManager">Instance of the <see cref="ILiveTvManager"/> interface.</param>
|
/// <param name="liveTvManager">Instance of the <see cref="ILiveTvManager"/> interface.</param>
|
||||||
/// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
|
/// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
|
||||||
/// <param name="httpClient">Instance of the <see cref="IHttpClient"/> interface.</param>
|
/// <param name="httpClientFactory">Instance of the <see cref="IHttpClientFactory"/> interface.</param>
|
||||||
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
|
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
|
||||||
/// <param name="dtoService">Instance of the <see cref="IDtoService"/> interface.</param>
|
/// <param name="dtoService">Instance of the <see cref="IDtoService"/> interface.</param>
|
||||||
/// <param name="sessionContext">Instance of the <see cref="ISessionContext"/> interface.</param>
|
/// <param name="sessionContext">Instance of the <see cref="ISessionContext"/> interface.</param>
|
||||||
|
@ -62,7 +62,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
public LiveTvController(
|
public LiveTvController(
|
||||||
ILiveTvManager liveTvManager,
|
ILiveTvManager liveTvManager,
|
||||||
IUserManager userManager,
|
IUserManager userManager,
|
||||||
IHttpClient httpClient,
|
IHttpClientFactory httpClientFactory,
|
||||||
ILibraryManager libraryManager,
|
ILibraryManager libraryManager,
|
||||||
IDtoService dtoService,
|
IDtoService dtoService,
|
||||||
ISessionContext sessionContext,
|
ISessionContext sessionContext,
|
||||||
|
@ -72,7 +72,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
{
|
{
|
||||||
_liveTvManager = liveTvManager;
|
_liveTvManager = liveTvManager;
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_httpClient = httpClient;
|
_httpClientFactory = httpClientFactory;
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
_dtoService = dtoService;
|
_dtoService = dtoService;
|
||||||
_sessionContext = sessionContext;
|
_sessionContext = sessionContext;
|
||||||
|
@ -1069,13 +1069,12 @@ namespace Jellyfin.Api.Controllers
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
public async Task<ActionResult> GetSchedulesDirectCountries()
|
public async Task<ActionResult> GetSchedulesDirectCountries()
|
||||||
{
|
{
|
||||||
|
var client = _httpClientFactory.CreateClient();
|
||||||
// https://json.schedulesdirect.org/20141201/available/countries
|
// https://json.schedulesdirect.org/20141201/available/countries
|
||||||
var response = await _httpClient.Get(new HttpRequestOptions
|
using var response = await client.GetAsync("https://json.schedulesdirect.org/20141201/available/countries")
|
||||||
{
|
.ConfigureAwait(false);
|
||||||
Url = "https://json.schedulesdirect.org/20141201/available/countries",
|
|
||||||
BufferContent = false
|
return File(await response.Content.ReadAsStreamAsync().ConfigureAwait(false), MediaTypeNames.Application.Json);
|
||||||
}).ConfigureAwait(false);
|
|
||||||
return File(response, MediaTypeNames.Application.Json);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -3,12 +3,12 @@ using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
using System.Net.Mime;
|
using System.Net.Mime;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Jellyfin.Api.Constants;
|
using Jellyfin.Api.Constants;
|
||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Common.Net;
|
|
||||||
using MediaBrowser.Controller;
|
using MediaBrowser.Controller;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
|
@ -30,7 +30,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
{
|
{
|
||||||
private readonly IProviderManager _providerManager;
|
private readonly IProviderManager _providerManager;
|
||||||
private readonly IServerApplicationPaths _applicationPaths;
|
private readonly IServerApplicationPaths _applicationPaths;
|
||||||
private readonly IHttpClient _httpClient;
|
private readonly IHttpClientFactory _httpClientFactory;
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -38,17 +38,17 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
|
/// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
|
||||||
/// <param name="applicationPaths">Instance of the <see cref="IServerApplicationPaths"/> interface.</param>
|
/// <param name="applicationPaths">Instance of the <see cref="IServerApplicationPaths"/> interface.</param>
|
||||||
/// <param name="httpClient">Instance of the <see cref="IHttpClient"/> interface.</param>
|
/// <param name="httpClientFactory">Instance of the <see cref="IHttpClientFactory"/> interface.</param>
|
||||||
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
|
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
|
||||||
public RemoteImageController(
|
public RemoteImageController(
|
||||||
IProviderManager providerManager,
|
IProviderManager providerManager,
|
||||||
IServerApplicationPaths applicationPaths,
|
IServerApplicationPaths applicationPaths,
|
||||||
IHttpClient httpClient,
|
IHttpClientFactory httpClientFactory,
|
||||||
ILibraryManager libraryManager)
|
ILibraryManager libraryManager)
|
||||||
{
|
{
|
||||||
_providerManager = providerManager;
|
_providerManager = providerManager;
|
||||||
_applicationPaths = applicationPaths;
|
_applicationPaths = applicationPaths;
|
||||||
_httpClient = httpClient;
|
_httpClientFactory = httpClientFactory;
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,22 +244,14 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
private async Task DownloadImage(string url, Guid urlHash, string pointerCachePath)
|
private async Task DownloadImage(string url, Guid urlHash, string pointerCachePath)
|
||||||
{
|
{
|
||||||
using var result = await _httpClient.GetResponse(new HttpRequestOptions
|
var httpClient = _httpClientFactory.CreateClient();
|
||||||
{
|
using var response = await httpClient.GetAsync(url).ConfigureAwait(false);
|
||||||
Url = url,
|
var ext = response.Content.Headers.ContentType.MediaType.Split('/').Last();
|
||||||
BufferContent = false
|
|
||||||
}).ConfigureAwait(false);
|
|
||||||
var ext = result.ContentType.Split('/').Last();
|
|
||||||
|
|
||||||
var fullCachePath = GetFullCachePath(urlHash + "." + ext);
|
var fullCachePath = GetFullCachePath(urlHash + "." + ext);
|
||||||
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath));
|
Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath));
|
||||||
await using (var stream = result.Content)
|
await using var fileStream = new FileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true);
|
||||||
{
|
await response.Content.CopyToAsync(fileStream).ConfigureAwait(false);
|
||||||
await using var fileStream = new FileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true);
|
|
||||||
await stream.CopyToAsync(fileStream).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(pointerCachePath));
|
Directory.CreateDirectory(Path.GetDirectoryName(pointerCachePath));
|
||||||
await System.IO.File.WriteAllTextAsync(pointerCachePath, fullCachePath, CancellationToken.None)
|
await System.IO.File.WriteAllTextAsync(pointerCachePath, fullCachePath, CancellationToken.None)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
|
@ -470,8 +470,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
{
|
{
|
||||||
StreamingHelpers.AddDlnaHeaders(state, Response.Headers, true, startTimeTicks, Request, _dlnaManager);
|
StreamingHelpers.AddDlnaHeaders(state, Response.Headers, true, startTimeTicks, Request, _dlnaManager);
|
||||||
|
|
||||||
using var httpClient = _httpClientFactory.CreateClient();
|
var httpClient = _httpClientFactory.CreateClient();
|
||||||
return await FileStreamResponseHelpers.GetStaticRemoteStreamResult(state, isHeadRequest, httpClient, HttpContext).ConfigureAwait(false);
|
return await FileStreamResponseHelpers.GetStaticRemoteStreamResult(state, isHeadRequest, this, httpClient).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (@static.HasValue && @static.Value && state.InputProtocol != MediaProtocol.File)
|
if (@static.HasValue && @static.Value && state.InputProtocol != MediaProtocol.File)
|
||||||
|
|
|
@ -154,6 +154,7 @@ namespace Jellyfin.Server.Extensions
|
||||||
opts.OutputFormatters.Insert(0, new PascalCaseJsonProfileFormatter());
|
opts.OutputFormatters.Insert(0, new PascalCaseJsonProfileFormatter());
|
||||||
|
|
||||||
opts.OutputFormatters.Add(new CssOutputFormatter());
|
opts.OutputFormatters.Add(new CssOutputFormatter());
|
||||||
|
opts.OutputFormatters.Add(new XmlOutputFormatter());
|
||||||
})
|
})
|
||||||
|
|
||||||
// Clear app parts to avoid other assemblies being picked up
|
// Clear app parts to avoid other assemblies being picked up
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
using System.Net.Mime;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Formatters;
|
||||||
|
|
||||||
|
namespace Jellyfin.Server.Formatters
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Xml output formatter.
|
||||||
|
/// </summary>
|
||||||
|
public class XmlOutputFormatter : TextOutputFormatter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="XmlOutputFormatter"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public XmlOutputFormatter()
|
||||||
|
{
|
||||||
|
SupportedMediaTypes.Add(MediaTypeNames.Text.Xml);
|
||||||
|
SupportedMediaTypes.Add("text/xml;charset=UTF-8");
|
||||||
|
SupportedEncodings.Add(Encoding.UTF8);
|
||||||
|
SupportedEncodings.Add(Encoding.Unicode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
|
||||||
|
{
|
||||||
|
return context.HttpContext.Response.WriteAsync(context.Object?.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -344,11 +344,24 @@ namespace Jellyfin.Server
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind to unix socket (only on OSX and Linux)
|
// Bind to unix socket (only on macOS and Linux)
|
||||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if (startupConfig.UseUnixSocket() && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
{
|
{
|
||||||
// TODO: allow configuration of socket path
|
var socketPath = startupConfig.GetUnixSocketPath();
|
||||||
var socketPath = $"{appPaths.DataPath}/socket.sock";
|
if (string.IsNullOrEmpty(socketPath))
|
||||||
|
{
|
||||||
|
var xdgRuntimeDir = Environment.GetEnvironmentVariable("XDG_RUNTIME_DIR");
|
||||||
|
if (xdgRuntimeDir == null)
|
||||||
|
{
|
||||||
|
// Fall back to config dir
|
||||||
|
socketPath = Path.Join(appPaths.ConfigurationDirectoryPath, "socket.sock");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
socketPath = Path.Join(xdgRuntimeDir, "jellyfin-socket");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Workaround for https://github.com/aspnet/AspNetCore/issues/14134
|
// Workaround for https://github.com/aspnet/AspNetCore/issues/14134
|
||||||
if (File.Exists(socketPath))
|
if (File.Exists(socketPath))
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,7 +8,7 @@ using System.Text.Json.Serialization;
|
||||||
namespace MediaBrowser.Common.Json.Converters
|
namespace MediaBrowser.Common.Json.Converters
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Long to String JSON converter.
|
/// Parse JSON string as long.
|
||||||
/// Javascript does not support 64-bit integers.
|
/// Javascript does not support 64-bit integers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class JsonInt64Converter : JsonConverter<long>
|
public class JsonInt64Converter : JsonConverter<long>
|
||||||
|
@ -43,14 +43,14 @@ namespace MediaBrowser.Common.Json.Converters
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Write long to JSON string.
|
/// Write long to JSON long.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="writer"><see cref="Utf8JsonWriter"/>.</param>
|
/// <param name="writer"><see cref="Utf8JsonWriter"/>.</param>
|
||||||
/// <param name="value">Value to write.</param>
|
/// <param name="value">Value to write.</param>
|
||||||
/// <param name="options">Options.</param>
|
/// <param name="options">Options.</param>
|
||||||
public override void Write(Utf8JsonWriter writer, long value, JsonSerializerOptions options)
|
public override void Write(Utf8JsonWriter writer, long value, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
writer.WriteStringValue(value.ToString(NumberFormatInfo.InvariantInfo));
|
writer.WriteNumberValue(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ using System.Collections.Generic;
|
||||||
using Jellyfin.Data.Entities;
|
using Jellyfin.Data.Entities;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Model.Dto;
|
using MediaBrowser.Model.Dto;
|
||||||
using MediaBrowser.Model.Querying;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Dto
|
namespace MediaBrowser.Controller.Dto
|
||||||
{
|
{
|
||||||
|
@ -11,20 +10,6 @@ namespace MediaBrowser.Controller.Dto
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IDtoService
|
public interface IDtoService
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Gets the dto id.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="item">The item.</param>
|
|
||||||
/// <returns>System.String.</returns>
|
|
||||||
string GetDtoId(BaseItem item);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Attaches the primary image aspect ratio.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dto">The dto.</param>
|
|
||||||
/// <param name="item">The item.</param>
|
|
||||||
void AttachPrimaryImageAspectRatio(IItemDto dto, BaseItem item);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the primary image aspect ratio.
|
/// Gets the primary image aspect ratio.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -32,15 +17,6 @@ namespace MediaBrowser.Controller.Dto
|
||||||
/// <returns>System.Nullable<System.Double>.</returns>
|
/// <returns>System.Nullable<System.Double>.</returns>
|
||||||
double? GetPrimaryImageAspectRatio(BaseItem item);
|
double? GetPrimaryImageAspectRatio(BaseItem item);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the base item dto.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="item">The item.</param>
|
|
||||||
/// <param name="fields">The fields.</param>
|
|
||||||
/// <param name="user">The user.</param>
|
|
||||||
/// <param name="owner">The owner.</param>
|
|
||||||
BaseItemDto GetBaseItemDto(BaseItem item, ItemFields[] fields, User user = null, BaseItem owner = null);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the base item dto.
|
/// Gets the base item dto.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -33,6 +33,16 @@ namespace MediaBrowser.Controller.Extensions
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string PlaylistsAllowDuplicatesKey = "playlists:allowDuplicates";
|
public const string PlaylistsAllowDuplicatesKey = "playlists:allowDuplicates";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The key for a setting that indicates whether kestrel should bind to a unix socket.
|
||||||
|
/// </summary>
|
||||||
|
public const string BindToUnixSocketKey = "kestrel:socket";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The key for the unix socket path.
|
||||||
|
/// </summary>
|
||||||
|
public const string UnixSocketPathKey = "kestrel:socketPath";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether the application should host static web content from the <see cref="IConfiguration"/>.
|
/// Gets a value indicating whether the application should host static web content from the <see cref="IConfiguration"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -65,5 +75,21 @@ namespace MediaBrowser.Controller.Extensions
|
||||||
/// <returns>True if playlists should allow duplicates, otherwise false.</returns>
|
/// <returns>True if playlists should allow duplicates, otherwise false.</returns>
|
||||||
public static bool DoPlaylistsAllowDuplicates(this IConfiguration configuration)
|
public static bool DoPlaylistsAllowDuplicates(this IConfiguration configuration)
|
||||||
=> configuration.GetValue<bool>(PlaylistsAllowDuplicatesKey);
|
=> configuration.GetValue<bool>(PlaylistsAllowDuplicatesKey);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether kestrel should bind to a unix socket from the <see cref="IConfiguration" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configuration">The configuration to read the setting from.</param>
|
||||||
|
/// <returns><c>true</c> if kestrel should bind to a unix socket, otherwise <c>false</c>.</returns>
|
||||||
|
public static bool UseUnixSocket(this IConfiguration configuration)
|
||||||
|
=> configuration.GetValue<bool>(BindToUnixSocketKey);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the path for the unix socket from the <see cref="IConfiguration" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configuration">The configuration to read the setting from.</param>
|
||||||
|
/// <returns>The unix socket path.</returns>
|
||||||
|
public static string GetUnixSocketPath(this IConfiguration configuration)
|
||||||
|
=> configuration[UnixSocketPathKey];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
#nullable disable
|
|
||||||
#pragma warning disable CS1591
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Model.Extensions
|
|
||||||
{
|
|
||||||
// TODO: @bond remove
|
|
||||||
public static class ListHelper
|
|
||||||
{
|
|
||||||
public static bool ContainsIgnoreCase(string[] list, string value)
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var item in list)
|
|
||||||
{
|
|
||||||
if (string.Equals(item, value, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace MediaBrowser.Model.IO
|
namespace MediaBrowser.Model.IO
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -14,5 +16,10 @@ namespace MediaBrowser.Model.IO
|
||||||
/// The default file stream buffer size.
|
/// The default file stream buffer size.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const int FileStreamBufferSize = 4096;
|
public const int FileStreamBufferSize = 4096;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The default <see cref="StreamWriter" /> buffer size.
|
||||||
|
/// </summary>
|
||||||
|
public const int StreamWriterBufferSize = 1024;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,13 +124,16 @@ namespace MediaBrowser.Providers.Manager
|
||||||
var retryPaths = GetSavePaths(item, type, imageIndex, mimeType, false);
|
var retryPaths = GetSavePaths(item, type, imageIndex, mimeType, false);
|
||||||
|
|
||||||
// If there are more than one output paths, the stream will need to be seekable
|
// If there are more than one output paths, the stream will need to be seekable
|
||||||
var memoryStream = new MemoryStream();
|
if (paths.Length > 1 && !source.CanSeek)
|
||||||
await using (source.ConfigureAwait(false))
|
|
||||||
{
|
{
|
||||||
await source.CopyToAsync(memoryStream).ConfigureAwait(false);
|
var memoryStream = new MemoryStream();
|
||||||
}
|
await using (source.ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
await source.CopyToAsync(memoryStream).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
source = memoryStream;
|
source = memoryStream;
|
||||||
|
}
|
||||||
|
|
||||||
var currentImage = GetCurrentImage(item, type, index);
|
var currentImage = GetCurrentImage(item, type, index);
|
||||||
var currentImageIsLocalFile = currentImage != null && currentImage.IsLocalFile;
|
var currentImageIsLocalFile = currentImage != null && currentImage.IsLocalFile;
|
||||||
|
@ -140,20 +143,21 @@ namespace MediaBrowser.Providers.Manager
|
||||||
|
|
||||||
await using (source.ConfigureAwait(false))
|
await using (source.ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
var currentPathIndex = 0;
|
for (int i = 0; i < paths.Length; i++)
|
||||||
|
|
||||||
foreach (var path in paths)
|
|
||||||
{
|
{
|
||||||
source.Position = 0;
|
if (i != 0)
|
||||||
|
{
|
||||||
|
source.Position = 0;
|
||||||
|
}
|
||||||
|
|
||||||
string retryPath = null;
|
string retryPath = null;
|
||||||
if (paths.Length == retryPaths.Length)
|
if (paths.Length == retryPaths.Length)
|
||||||
{
|
{
|
||||||
retryPath = retryPaths[currentPathIndex];
|
retryPath = retryPaths[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
var savedPath = await SaveImageToLocation(source, path, retryPath, cancellationToken).ConfigureAwait(false);
|
var savedPath = await SaveImageToLocation(source, paths[i], retryPath, cancellationToken).ConfigureAwait(false);
|
||||||
savedPaths.Add(savedPath);
|
savedPaths.Add(savedPath);
|
||||||
currentPathIndex++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,7 +228,6 @@ namespace MediaBrowser.Providers.Manager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
source.Position = 0;
|
|
||||||
await SaveImageToLocation(source, retryPath, cancellationToken).ConfigureAwait(false);
|
await SaveImageToLocation(source, retryPath, cancellationToken).ConfigureAwait(false);
|
||||||
return retryPath;
|
return retryPath;
|
||||||
}
|
}
|
||||||
|
@ -253,7 +256,7 @@ namespace MediaBrowser.Providers.Manager
|
||||||
|
|
||||||
await using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous))
|
await using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous))
|
||||||
{
|
{
|
||||||
await source.CopyToAsync(fs, IODefaults.CopyToBufferSize, cancellationToken).ConfigureAwait(false);
|
await source.CopyToAsync(fs, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_config.Configuration.SaveMetadataHidden)
|
if (_config.Configuration.SaveMetadataHidden)
|
||||||
|
|
|
@ -148,7 +148,7 @@ namespace MediaBrowser.Providers.Subtitles
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var parts = subtitleId.Split(new[] { '_' }, 2);
|
var parts = subtitleId.Split(new[] { '_' }, 2);
|
||||||
var provider = GetProvider(parts.First());
|
var provider = GetProvider(parts[0]);
|
||||||
|
|
||||||
var saveInMediaFolder = libraryOptions.SaveSubtitlesWithMedia;
|
var saveInMediaFolder = libraryOptions.SaveSubtitlesWithMedia;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue