Merge remote-tracking branch 'upstream/master' into fixes

This commit is contained in:
crobibero 2020-08-17 15:54:40 -06:00
commit 29d8e38161
28 changed files with 255 additions and 250 deletions

View File

@ -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;
}
} }
} }

View File

@ -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;

View File

@ -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 }
}; };
} }
} }

View File

@ -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

View File

@ -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,

View File

@ -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);
} }
} }
} }

View File

@ -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);

View File

@ -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;
}
} }
} }

View File

@ -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

View File

@ -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;

View File

@ -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": "প্রিয় গানগুলো",

View File

@ -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} ஐ இயக்குகிறது"
} }

View File

@ -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);
} }

View File

@ -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")]

View File

@ -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)

View File

@ -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>

View File

@ -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);

View File

@ -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)

View 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

View File

@ -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());
}
}
}

View File

@ -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))
{ {

View File

@ -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);
} }
} }
} }

View File

@ -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&lt;System.Double&gt;.</returns> /// <returns>System.Nullable&lt;System.Double&gt;.</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>

View File

@ -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];
} }
} }

View File

@ -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;
}
}
}

View File

@ -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;
} }
} }

View File

@ -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)

View File

@ -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;