Compare commits

...

34 Commits

Author SHA1 Message Date
Joshua M. Boniface
595a68b822 Bump version for 10.3.7 2019-07-24 10:48:35 -04:00
Anthony Lavado
c5d9480313 Merge pull request #1552 from cvium/fix_livetv_v2
Disable buffering in HttpClient as it causes big requests to timeout
2019-07-22 02:28:38 -04:00
Claus Vium
dadfc09c01 Add HttpCompletionOption.ResponseHeadersRead to the buffering option to avoid potentially having 2 copies in memory 2019-07-20 13:36:59 +02:00
Claus Vium
886c88576c Use HttpCompletionOption.ResponseHeadersRead and resort to Content-Length header for non-buffered content 2019-07-19 23:22:30 +02:00
Joshua M. Boniface
cf2f5b2026 Merge pull request #1538 from joshuaboniface/epg
Try to fix XmlTvListingsProvider
2019-07-14 17:09:00 -04:00
Joshua M. Boniface
135c16c721 Merge pull request #1537 from joshuaboniface/contenttype
Properly set content type
2019-07-14 17:08:54 -04:00
Bond_009
5d9fa06675 Cleanup 2019-07-13 17:18:39 -04:00
Bond_009
b294b802a8 Try to fix XmlTvListingsProvider 2019-07-13 17:18:27 -04:00
Bond_009
7bb504d491 Create a new HttpMethod from the function name 2019-07-13 17:12:06 -04:00
Bond_009
b1bd062709 Properly set content type 2019-07-13 17:12:06 -04:00
Joshua M. Boniface
4f17ed961e Merge pull request #1514 from Bond-009/httpclient2
Fix issues with HttpClientManager
2019-07-06 17:01:35 -04:00
Bond_009
5fc4ad6c4e Address comments 2019-07-06 20:04:45 +02:00
Bond_009
b117b364f2 Remove duplicate code 2019-07-06 20:04:45 +02:00
Bond_009
3603c64fa6 Use HttpResponseHeaders instead of a dictionary 2019-07-06 20:04:45 +02:00
Bond_009
d405a400aa Fixes issues with HttpClientManager 2019-07-06 20:04:42 +02:00
Joshua M. Boniface
54c6f02ebb Merge pull request #1455 from ferferga/release-10.3.z
Vacuum databases at startup
2019-07-06 13:57:18 -04:00
Joshua M. Boniface
b3f9d04501 Bump version for 10.3.6 2019-07-06 13:34:38 -04:00
Erwin de Haan
acf52b9b55 Cleanup extra spaces. 2019-07-04 20:55:49 +02:00
Erwin de Haan
7587fe56d8 Moved VACUUM down to the end of the list. 2019-07-04 20:54:57 +02:00
Anthony Lavado
ab34a95142 Merge pull request #1473 from DrPandemic/apply-deprecation-WebRequest-on-10.3.z
Apply deprecation web request on 10.3.z
2019-07-04 14:51:24 -04:00
Andrew Rabert
0ee40cb636 Merge pull request #1495 from joshuaboniface/better-restart-script
Add nicer restart script
2019-06-29 19:48:54 -04:00
Joshua M. Boniface
62105c249f Use which to find the service binary path 2019-06-28 11:15:08 -04:00
Joshua M. Boniface
a629f209b9 Make message wording more consistent 2019-06-28 11:06:55 -04:00
Joshua M. Boniface
c08c0272b5 Add nicer restart script
The old restart script was buggy, as reported in #1320. This updated
script seems to work far more reliably and conforms to the existing
jellyfin-sudoers packages sudo configuration.
2019-06-27 18:05:03 -04:00
dkanada
c52e8a2027 Merge pull request #1394 from joern-h/bugfix-issue1347
Check if an item is a child of an EnabledFolder
2019-06-26 01:19:34 -07:00
Jörn
1fd8164756 fix issue #1347 introduced in pr #930 2019-06-26 01:13:54 -07:00
dkanada
3c16c34386 Merge pull request #1485 from DrPandemic/fix-skia-segfault
Fix skia segfault
2019-06-25 12:44:29 -05:00
DrPandemic
394d96246b Check path before opening image 2019-06-24 20:13:07 -04:00
Anthony Lavado
084854d71d Merge pull request #1478 from cvium/fix_tvdb_again
Wait for the async authentication to finish when the JTW token expires
2019-06-22 02:08:46 -04:00
Claus Vium
c2ab0ad641 Wait for the async authentication to finish when the JTW token expires 2019-06-21 19:08:04 +02:00
Claus Vium
7eb94e9674 Update MediaBrowser.Common/Net/IHttpClient.cs
Co-Authored-By: Bond-009 <bond.009@outlook.com>
2019-06-18 22:21:07 -04:00
Bond-009
0a5550b13d Remove more unused stuff 2019-06-18 22:20:34 -04:00
Bond-009
067200be83 Remove usage of depricated 'WebRequest'
Ref: https://docs.microsoft.com/en-us/dotnet/api/system.net.webrequest?view=netframework-4.7.2
2019-06-17 19:35:05 -04:00
ferferga
b136f14084 Vacuum databases at startup 2019-06-10 11:31:38 +02:00
29 changed files with 389 additions and 611 deletions

View File

@@ -23,8 +23,10 @@
- [fruhnow](https://github.com/fruhnow)
- [Lynxy](https://github.com/Lynxy)
- [fasheng](https://github.com/fasheng)
- [ploughpuff](https://github.com/ploughpuff)
- [ploughpuff](https://github.com/ploughpuff)
- [pjeanjean](https://github.com/pjeanjean)
- [DrPandemic](https://github.com/drpandemic)
- [joern-h](https://github.com/joern-h)
# Emby Contributors

View File

@@ -21,7 +21,7 @@ RUN apt-get update \
COPY --from=ffmpeg / /
COPY --from=builder /jellyfin /jellyfin
ARG JELLYFIN_WEB_VERSION=10.3.5
ARG JELLYFIN_WEB_VERSION=10.3.7
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& rm -rf /jellyfin/jellyfin-web \
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web

View File

@@ -26,7 +26,7 @@ RUN apt-get update \
&& chmod 777 /cache /config /media
COPY --from=builder /jellyfin /jellyfin
ARG JELLYFIN_WEB_VERSION=10.3.5
ARG JELLYFIN_WEB_VERSION=10.3.7
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& rm -rf /jellyfin/jellyfin-web \
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web

View File

@@ -26,7 +26,7 @@ RUN apt-get update \
&& chmod 777 /cache /config /media
COPY --from=builder /jellyfin /jellyfin
ARG JELLYFIN_WEB_VERSION=10.3.5
ARG JELLYFIN_WEB_VERSION=10.3.7
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& rm -rf /jellyfin/jellyfin-web \
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web

View File

@@ -34,16 +34,13 @@ namespace Emby.Dlna.PlayTo
{
var cancellationToken = CancellationToken.None;
using (var response = await PostSoapDataAsync(NormalizeServiceUrl(baseUrl, service.ControlUrl), "\"" + service.ServiceType + "#" + command + "\"", postData, header, logRequest, cancellationToken)
var url = NormalizeServiceUrl(baseUrl, service.ControlUrl);
using (var response = await PostSoapDataAsync(url, '\"' + service.ServiceType + '#' + command + '\"', postData, header, logRequest, cancellationToken)
.ConfigureAwait(false))
using (var stream = response.Content)
using (var reader = new StreamReader(stream, Encoding.UTF8))
{
using (var stream = response.Content)
{
using (var reader = new StreamReader(stream, Encoding.UTF8))
{
return XDocument.Parse(reader.ReadToEnd(), LoadOptions.PreserveWhitespace);
}
}
return XDocument.Parse(reader.ReadToEnd(), LoadOptions.PreserveWhitespace);
}
}
@@ -121,15 +118,18 @@ namespace Emby.Dlna.PlayTo
}
}
private Task<HttpResponseInfo> PostSoapDataAsync(string url,
private Task<HttpResponseInfo> PostSoapDataAsync(
string url,
string soapAction,
string postData,
string header,
bool logRequest,
CancellationToken cancellationToken)
{
if (!soapAction.StartsWith("\""))
soapAction = "\"" + soapAction + "\"";
if (soapAction[0] != '\"')
{
soapAction = '\"' + soapAction + '\"';
}
var options = new HttpRequestOptions
{
@@ -155,7 +155,6 @@ namespace Emby.Dlna.PlayTo
}
options.RequestContentType = "text/xml";
options.AppendCharsetToMimeType = true;
options.RequestContent = postData;
return _httpClient.Post(options);

View File

@@ -6,6 +6,7 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
@@ -429,7 +430,7 @@ namespace Emby.Server.Implementations
/// Gets the current application user agent
/// </summary>
/// <value>The application user agent.</value>
public string ApplicationUserAgent => Name.Replace(' ','-') + "/" + ApplicationVersion;
public string ApplicationUserAgent => Name.Replace(' ','-') + '/' + ApplicationVersion;
/// <summary>
/// Gets the email address for use within a comment section of a user agent field.
@@ -689,11 +690,6 @@ namespace Emby.Server.Implementations
await HttpServer.RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, CancellationToken.None).ConfigureAwait(false);
}
protected virtual IHttpClient CreateHttpClient()
{
return new HttpClientManager.HttpClientManager(ApplicationPaths, LoggerFactory, FileSystemManager, () => ApplicationUserAgent);
}
public static IStreamHelper StreamHelper { get; set; }
/// <summary>
@@ -719,7 +715,11 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton(FileSystemManager);
serviceCollection.AddSingleton<TvDbClientManager>();
HttpClient = CreateHttpClient();
HttpClient = new HttpClientManager.HttpClientManager(
ApplicationPaths,
LoggerFactory.CreateLogger<HttpClientManager.HttpClientManager>(),
FileSystemManager,
() => ApplicationUserAgent);
serviceCollection.AddSingleton(HttpClient);
serviceCollection.AddSingleton(NetworkManager);
@@ -1560,12 +1560,12 @@ namespace Emby.Server.Implementations
LogErrorResponseBody = false,
LogErrors = false,
LogRequest = false,
TimeoutMs = 10000,
BufferContent = false,
CancellationToken = cancellationToken
}).ConfigureAwait(false))
{
return GetWanApiUrl(response.ReadToEnd().Trim());
string res = await response.ReadToEndAsync().ConfigureAwait(false);
return GetWanApiUrl(res.Trim());
}
}
catch (Exception ex)
@@ -1717,15 +1717,15 @@ namespace Emby.Server.Implementations
LogErrorResponseBody = false,
LogErrors = LogPing,
LogRequest = LogPing,
TimeoutMs = 5000,
BufferContent = false,
CancellationToken = cancellationToken
}, "POST").ConfigureAwait(false))
}, HttpMethod.Post).ConfigureAwait(false))
{
using (var reader = new StreamReader(response.Content))
{
var result = reader.ReadToEnd();
var result = await reader.ReadToEndAsync().ConfigureAwait(false);
var valid = string.Equals(Name, result, StringComparison.OrdinalIgnoreCase);
_validAddressResults.AddOrUpdate(apiUrl, valid, (k, v) => valid);

View File

@@ -223,6 +223,8 @@ namespace Emby.Server.Implementations.Data
"pragma temp_store = file"
});
}
// Configuration and pragmas can affect VACUUM so it needs to be last.
queries.Add("VACUUM");
db.ExecuteAll(string.Join(";", queries));
Logger.LogInformation("PRAGMA synchronous=" + db.Query("PRAGMA synchronous").SelectScalarString().First());

View File

@@ -1,18 +0,0 @@
using System;
using System.Net.Http;
namespace Emby.Server.Implementations.HttpClientManager
{
/// <summary>
/// Class HttpClientInfo
/// </summary>
public class HttpClientInfo
{
/// <summary>
/// Gets or sets the last timeout.
/// </summary>
/// <value>The last timeout.</value>
public DateTime LastTimeout { get; set; }
public HttpClient HttpClient { get; set; }
}
}

View File

@@ -1,12 +1,9 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Cache;
using System.Text;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
@@ -24,30 +21,24 @@ namespace Emby.Server.Implementations.HttpClientManager
/// </summary>
public class HttpClientManager : IHttpClient
{
/// <summary>
/// When one request to a host times out, we'll ban all other requests for this period of time, to prevent scans from stalling
/// </summary>
private const int TimeoutSeconds = 30;
/// <summary>
/// The _logger
/// </summary>
private readonly ILogger _logger;
/// <summary>
/// The _app paths
/// </summary>
private readonly IApplicationPaths _appPaths;
private readonly IFileSystem _fileSystem;
private readonly Func<string> _defaultUserAgentFn;
/// <summary>
/// Holds a dictionary of http clients by host. Use GetHttpClient(host) to retrieve or create a client for web requests.
/// DON'T dispose it after use.
/// </summary>
/// <value>The HTTP clients.</value>
private readonly ConcurrentDictionary<string, HttpClient> _httpClients = new ConcurrentDictionary<string, HttpClient>();
/// <summary>
/// Initializes a new instance of the <see cref="HttpClientManager" /> class.
/// </summary>
public HttpClientManager(
IApplicationPaths appPaths,
ILoggerFactory loggerFactory,
ILogger<HttpClientManager> logger,
IFileSystem fileSystem,
Func<string> defaultUserAgentFn)
{
@@ -55,46 +46,33 @@ namespace Emby.Server.Implementations.HttpClientManager
{
throw new ArgumentNullException(nameof(appPaths));
}
if (loggerFactory == null)
if (logger == null)
{
throw new ArgumentNullException(nameof(loggerFactory));
throw new ArgumentNullException(nameof(logger));
}
_logger = loggerFactory.CreateLogger("HttpClient");
_logger = logger;
_fileSystem = fileSystem;
_appPaths = appPaths;
_defaultUserAgentFn = defaultUserAgentFn;
// http://stackoverflow.com/questions/566437/http-post-returns-the-error-417-expectation-failed-c
ServicePointManager.Expect100Continue = false;
}
/// <summary>
/// Holds a dictionary of http clients by host. Use GetHttpClient(host) to retrieve or create a client for web requests.
/// DON'T dispose it after use.
/// Gets the correct http client for the given url.
/// </summary>
/// <value>The HTTP clients.</value>
private readonly ConcurrentDictionary<string, HttpClientInfo> _httpClients = new ConcurrentDictionary<string, HttpClientInfo>();
/// <summary>
/// Gets
/// </summary>
/// <param name="host">The host.</param>
/// <param name="enableHttpCompression">if set to <c>true</c> [enable HTTP compression].</param>
/// <param name="url">The url.</param>
/// <returns>HttpClient.</returns>
/// <exception cref="ArgumentNullException">host</exception>
private HttpClientInfo GetHttpClient(string host, bool enableHttpCompression)
private HttpClient GetHttpClient(string url)
{
if (string.IsNullOrEmpty(host))
{
throw new ArgumentNullException(nameof(host));
}
var key = host + enableHttpCompression;
var key = GetHostFromUrl(url);
if (!_httpClients.TryGetValue(key, out var client))
{
client = new HttpClientInfo();
client = new HttpClient()
{
BaseAddress = new Uri(url)
};
_httpClients.TryAdd(key, client);
}
@@ -102,119 +80,84 @@ namespace Emby.Server.Implementations.HttpClientManager
return client;
}
private WebRequest GetRequest(HttpRequestOptions options, string method)
private HttpRequestMessage GetRequestMessage(HttpRequestOptions options, HttpMethod method)
{
string url = options.Url;
var uriAddress = new Uri(url);
string userInfo = uriAddress.UserInfo;
if (!string.IsNullOrWhiteSpace(userInfo))
{
_logger.LogInformation("Found userInfo in url: {0} ... url: {1}", userInfo, url);
url = url.Replace(userInfo + "@", string.Empty);
_logger.LogWarning("Found userInfo in url: {0} ... url: {1}", userInfo, url);
url = url.Replace(userInfo + '@', string.Empty);
}
var request = WebRequest.Create(url);
var request = new HttpRequestMessage(method, url);
if (request is HttpWebRequest httpWebRequest)
AddRequestHeaders(request, options);
switch (options.DecompressionMethod)
{
AddRequestHeaders(httpWebRequest, options);
if (options.EnableHttpCompression)
{
httpWebRequest.AutomaticDecompression = DecompressionMethods.Deflate;
if (options.DecompressionMethod.HasValue
&& options.DecompressionMethod.Value == CompressionMethod.Gzip)
{
httpWebRequest.AutomaticDecompression = DecompressionMethods.GZip;
}
}
else
{
httpWebRequest.AutomaticDecompression = DecompressionMethods.None;
}
httpWebRequest.KeepAlive = options.EnableKeepAlive;
if (!string.IsNullOrEmpty(options.Host))
{
httpWebRequest.Host = options.Host;
}
if (!string.IsNullOrEmpty(options.Referer))
{
httpWebRequest.Referer = options.Referer;
}
case CompressionMethod.Deflate | CompressionMethod.Gzip:
request.Headers.Add(HeaderNames.AcceptEncoding, new[] { "gzip", "deflate" });
break;
case CompressionMethod.Deflate:
request.Headers.Add(HeaderNames.AcceptEncoding, "deflate");
break;
case CompressionMethod.Gzip:
request.Headers.Add(HeaderNames.AcceptEncoding, "gzip");
break;
default:
break;
}
request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache);
if (options.EnableKeepAlive)
{
request.Headers.Add(HeaderNames.Connection, "Keep-Alive");
}
request.Method = method;
request.Timeout = options.TimeoutMs;
//request.Headers.Add(HeaderNames.CacheControl, "no-cache");
/*
if (!string.IsNullOrWhiteSpace(userInfo))
{
var parts = userInfo.Split(':');
if (parts.Length == 2)
{
request.Credentials = GetCredential(url, parts[0], parts[1]);
// TODO: .net core ??
request.PreAuthenticate = true;
request.Headers.Add(HeaderNames., GetCredential(url, parts[0], parts[1]);
}
}
*/
return request;
}
private static CredentialCache GetCredential(string url, string username, string password)
{
//ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
var credentialCache = new CredentialCache();
credentialCache.Add(new Uri(url), "Basic", new NetworkCredential(username, password));
return credentialCache;
}
private void AddRequestHeaders(HttpWebRequest request, HttpRequestOptions options)
private void AddRequestHeaders(HttpRequestMessage request, HttpRequestOptions options)
{
var hasUserAgent = false;
foreach (var header in options.RequestHeaders)
{
if (string.Equals(header.Key, HeaderNames.Accept, StringComparison.OrdinalIgnoreCase))
if (string.Equals(header.Key, HeaderNames.UserAgent, StringComparison.OrdinalIgnoreCase))
{
request.Accept = header.Value;
}
else if (string.Equals(header.Key, HeaderNames.UserAgent, StringComparison.OrdinalIgnoreCase))
{
SetUserAgent(request, header.Value);
hasUserAgent = true;
}
else
{
request.Headers.Set(header.Key, header.Value);
}
request.Headers.Add(header.Key, header.Value);
}
if (!hasUserAgent && options.EnableDefaultUserAgent)
{
SetUserAgent(request, _defaultUserAgentFn());
request.Headers.Add(HeaderNames.UserAgent, _defaultUserAgentFn());
}
}
private static void SetUserAgent(HttpWebRequest request, string userAgent)
{
request.UserAgent = userAgent;
}
/// <summary>
/// Gets the response internal.
/// </summary>
/// <param name="options">The options.</param>
/// <returns>Task{HttpResponseInfo}.</returns>
public Task<HttpResponseInfo> GetResponse(HttpRequestOptions options)
{
return SendAsync(options, "GET");
}
=> SendAsync(options, HttpMethod.Get);
/// <summary>
/// Performs a GET request and returns the resulting stream
@@ -233,9 +176,16 @@ namespace Emby.Server.Implementations.HttpClientManager
/// <param name="options">The options.</param>
/// <param name="httpMethod">The HTTP method.</param>
/// <returns>Task{HttpResponseInfo}.</returns>
/// <exception cref="HttpException">
/// </exception>
public async Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, string httpMethod)
public Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, string httpMethod)
=> SendAsync(options, new HttpMethod(httpMethod));
/// <summary>
/// send as an asynchronous operation.
/// </summary>
/// <param name="options">The options.</param>
/// <param name="httpMethod">The HTTP method.</param>
/// <returns>Task{HttpResponseInfo}.</returns>
public async Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, HttpMethod httpMethod)
{
if (options.CacheMode == CacheMode.None)
{
@@ -294,186 +244,89 @@ namespace Emby.Server.Implementations.HttpClientManager
}
}
private async Task<HttpResponseInfo> SendAsyncInternal(HttpRequestOptions options, string httpMethod)
private async Task<HttpResponseInfo> SendAsyncInternal(HttpRequestOptions options, HttpMethod httpMethod)
{
ValidateParams(options);
options.CancellationToken.ThrowIfCancellationRequested();
var client = GetHttpClient(GetHostFromUrl(options.Url), options.EnableHttpCompression);
var client = GetHttpClient(options.Url);
if ((DateTime.UtcNow - client.LastTimeout).TotalSeconds < TimeoutSeconds)
var httpWebRequest = GetRequestMessage(options, httpMethod);
if (options.RequestContentBytes != null
|| !string.IsNullOrEmpty(options.RequestContent)
|| httpMethod == HttpMethod.Post)
{
throw new HttpException(string.Format("Cancelling connection to {0} due to a previous timeout.", options.Url))
if (options.RequestContentBytes != null)
{
IsTimedOut = true
};
}
var httpWebRequest = GetRequest(options, httpMethod);
if (options.RequestContentBytes != null ||
!string.IsNullOrEmpty(options.RequestContent) ||
string.Equals(httpMethod, "post", StringComparison.OrdinalIgnoreCase))
{
try
{
var bytes = options.RequestContentBytes ?? Encoding.UTF8.GetBytes(options.RequestContent ?? string.Empty);
var contentType = options.RequestContentType ?? "application/x-www-form-urlencoded";
if (options.AppendCharsetToMimeType)
{
contentType = contentType.TrimEnd(';') + "; charset=\"utf-8\"";
}
httpWebRequest.ContentType = contentType;
(await httpWebRequest.GetRequestStreamAsync().ConfigureAwait(false)).Write(bytes, 0, bytes.Length);
httpWebRequest.Content = new ByteArrayContent(options.RequestContentBytes);
}
catch (Exception ex)
else if (options.RequestContent != null)
{
throw new HttpException(ex.Message) { IsTimedOut = true };
httpWebRequest.Content = new StringContent(
options.RequestContent,
null,
options.RequestContentType);
}
else
{
httpWebRequest.Content = new ByteArrayContent(Array.Empty<byte>());
}
}
if (options.ResourcePool != null)
{
await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false);
}
if ((DateTime.UtcNow - client.LastTimeout).TotalSeconds < TimeoutSeconds)
{
options.ResourcePool?.Release();
throw new HttpException($"Connection to {options.Url} timed out") { IsTimedOut = true };
}
if (options.LogRequest)
{
if (options.LogRequestAsDebug)
{
_logger.LogDebug("HttpClientManager {0}: {1}", httpMethod.ToUpper(CultureInfo.CurrentCulture), options.Url);
}
else
{
_logger.LogInformation("HttpClientManager {0}: {1}", httpMethod.ToUpper(CultureInfo.CurrentCulture), options.Url);
}
_logger.LogDebug("HttpClientManager {0}: {1}", httpMethod.ToString(), options.Url);
}
try
options.CancellationToken.ThrowIfCancellationRequested();
if (!options.BufferContent)
{
var response = await client.SendAsync(httpWebRequest, HttpCompletionOption.ResponseHeadersRead, options.CancellationToken).ConfigureAwait(false);
await EnsureSuccessStatusCode(response, options).ConfigureAwait(false);
options.CancellationToken.ThrowIfCancellationRequested();
if (!options.BufferContent)
var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
return new HttpResponseInfo(response.Headers, response.Content.Headers)
{
var response = await GetResponseAsync(httpWebRequest, TimeSpan.FromMilliseconds(options.TimeoutMs)).ConfigureAwait(false);
Content = stream,
StatusCode = response.StatusCode,
ContentType = response.Content.Headers.ContentType?.MediaType,
ContentLength = response.Content.Headers.ContentLength,
ResponseUrl = response.Content.Headers.ContentLocation?.ToString()
};
}
var httpResponse = (HttpWebResponse)response;
using (var response = await client.SendAsync(httpWebRequest, HttpCompletionOption.ResponseHeadersRead, options.CancellationToken).ConfigureAwait(false))
{
await EnsureSuccessStatusCode(response, options).ConfigureAwait(false);
EnsureSuccessStatusCode(client, httpResponse, options);
options.CancellationToken.ThrowIfCancellationRequested();
options.CancellationToken.ThrowIfCancellationRequested();
return GetResponseInfo(httpResponse, httpResponse.GetResponseStream(), GetContentLength(httpResponse), httpResponse);
}
using (var response = await GetResponseAsync(httpWebRequest, TimeSpan.FromMilliseconds(options.TimeoutMs)).ConfigureAwait(false))
using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
{
var httpResponse = (HttpWebResponse)response;
var memoryStream = new MemoryStream();
await stream.CopyToAsync(memoryStream, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false);
memoryStream.Position = 0;
EnsureSuccessStatusCode(client, httpResponse, options);
options.CancellationToken.ThrowIfCancellationRequested();
using (var stream = httpResponse.GetResponseStream())
return new HttpResponseInfo(response.Headers, response.Content.Headers)
{
var memoryStream = new MemoryStream();
await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
memoryStream.Position = 0;
return GetResponseInfo(httpResponse, memoryStream, memoryStream.Length, null);
}
Content = memoryStream,
StatusCode = response.StatusCode,
ContentType = response.Content.Headers.ContentType?.MediaType,
ContentLength = memoryStream.Length,
ResponseUrl = response.Content.Headers.ContentLocation?.ToString()
};
}
}
catch (OperationCanceledException ex)
{
throw GetCancellationException(options, client, options.CancellationToken, ex);
}
catch (Exception ex)
{
throw GetException(ex, options, client);
}
finally
{
options.ResourcePool?.Release();
}
}
private HttpResponseInfo GetResponseInfo(HttpWebResponse httpResponse, Stream content, long? contentLength, IDisposable disposable)
{
var responseInfo = new HttpResponseInfo(disposable)
{
Content = content,
StatusCode = httpResponse.StatusCode,
ContentType = httpResponse.ContentType,
ContentLength = contentLength,
ResponseUrl = httpResponse.ResponseUri.ToString()
};
if (httpResponse.Headers != null)
{
SetHeaders(httpResponse.Headers, responseInfo);
}
return responseInfo;
}
private HttpResponseInfo GetResponseInfo(HttpWebResponse httpResponse, string tempFile, long? contentLength)
{
var responseInfo = new HttpResponseInfo
{
TempFilePath = tempFile,
StatusCode = httpResponse.StatusCode,
ContentType = httpResponse.ContentType,
ContentLength = contentLength
};
if (httpResponse.Headers != null)
{
SetHeaders(httpResponse.Headers, responseInfo);
}
return responseInfo;
}
private static void SetHeaders(WebHeaderCollection headers, HttpResponseInfo responseInfo)
{
foreach (var key in headers.AllKeys)
{
responseInfo.Headers[key] = headers[key];
}
}
public Task<HttpResponseInfo> Post(HttpRequestOptions options)
{
return SendAsync(options, "POST");
}
/// <summary>
/// Performs a POST request
/// </summary>
/// <param name="options">The options.</param>
/// <param name="postData">Params to add to the POST data.</param>
/// <returns>stream on success, null on failure</returns>
public async Task<Stream> Post(HttpRequestOptions options, Dictionary<string, string> postData)
{
options.SetPostData(postData);
var response = await Post(options).ConfigureAwait(false);
return response.Content;
}
=> SendAsync(options, HttpMethod.Post);
/// <summary>
/// Downloads the contents of a given url into a temporary location
@@ -483,7 +336,6 @@ namespace Emby.Server.Implementations.HttpClientManager
public async Task<string> GetTempFile(HttpRequestOptions options)
{
var response = await GetTempFileResponse(options).ConfigureAwait(false);
return response.TempFilePath;
}
@@ -502,44 +354,28 @@ namespace Emby.Server.Implementations.HttpClientManager
options.CancellationToken.ThrowIfCancellationRequested();
var httpWebRequest = GetRequest(options, "GET");
if (options.ResourcePool != null)
{
await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false);
}
var httpWebRequest = GetRequestMessage(options, HttpMethod.Get);
options.Progress.Report(0);
if (options.LogRequest)
{
if (options.LogRequestAsDebug)
{
_logger.LogDebug("HttpClientManager.GetTempFileResponse url: {0}", options.Url);
}
else
{
_logger.LogInformation("HttpClientManager.GetTempFileResponse url: {0}", options.Url);
}
_logger.LogDebug("HttpClientManager.GetTempFileResponse url: {0}", options.Url);
}
var client = GetHttpClient(GetHostFromUrl(options.Url), options.EnableHttpCompression);
var client = GetHttpClient(options.Url);
try
{
options.CancellationToken.ThrowIfCancellationRequested();
using (var response = await httpWebRequest.GetResponseAsync().ConfigureAwait(false))
using (var response = (await client.SendAsync(httpWebRequest, options.CancellationToken).ConfigureAwait(false)))
{
var httpResponse = (HttpWebResponse)response;
EnsureSuccessStatusCode(client, httpResponse, options);
await EnsureSuccessStatusCode(response, options).ConfigureAwait(false);
options.CancellationToken.ThrowIfCancellationRequested();
var contentLength = GetContentLength(httpResponse);
using (var stream = httpResponse.GetResponseStream())
using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
using (var fs = _fileSystem.GetFileStream(tempFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
{
await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false);
@@ -547,35 +383,29 @@ namespace Emby.Server.Implementations.HttpClientManager
options.Progress.Report(100);
return GetResponseInfo(httpResponse, tempFile, contentLength);
var responseInfo = new HttpResponseInfo(response.Headers, response.Content.Headers)
{
TempFilePath = tempFile,
StatusCode = response.StatusCode,
ContentType = response.Content.Headers.ContentType?.MediaType,
ContentLength = response.Content.Headers.ContentLength
};
return responseInfo;
}
}
catch (Exception ex)
{
DeleteTempFile(tempFile);
throw GetException(ex, options, client);
}
finally
{
options.ResourcePool?.Release();
if (File.Exists(tempFile))
{
File.Delete(tempFile);
}
throw GetException(ex, options);
}
}
private static long? GetContentLength(HttpWebResponse response)
{
var length = response.ContentLength;
if (length == 0)
{
return null;
}
return length;
}
protected static readonly CultureInfo UsCulture = new CultureInfo("en-US");
private Exception GetException(Exception ex, HttpRequestOptions options, HttpClientInfo client)
private Exception GetException(Exception ex, HttpRequestOptions options)
{
if (ex is HttpException)
{
@@ -589,7 +419,7 @@ namespace Emby.Server.Implementations.HttpClientManager
{
if (options.LogErrors)
{
_logger.LogError(webException, "Error {status} getting response from {url}", webException.Status, options.Url);
_logger.LogError(webException, "Error {Status} getting response from {Url}", webException.Status, options.Url);
}
var exception = new HttpException(webException.Message, webException);
@@ -599,11 +429,6 @@ namespace Emby.Server.Implementations.HttpClientManager
if (response != null)
{
exception.StatusCode = response.StatusCode;
if ((int)response.StatusCode == 429)
{
client.LastTimeout = DateTime.UtcNow;
}
}
}
@@ -624,29 +449,17 @@ namespace Emby.Server.Implementations.HttpClientManager
if (operationCanceledException != null)
{
return GetCancellationException(options, client, options.CancellationToken, operationCanceledException);
return GetCancellationException(options, options.CancellationToken, operationCanceledException);
}
if (options.LogErrors)
{
_logger.LogError(ex, "Error getting response from {url}", options.Url);
_logger.LogError(ex, "Error getting response from {Url}", options.Url);
}
return ex;
}
private void DeleteTempFile(string file)
{
try
{
_fileSystem.DeleteFile(file);
}
catch (IOException)
{
// Might not have been created at all. No need to worry.
}
}
private void ValidateParams(HttpRequestOptions options)
{
if (string.IsNullOrEmpty(options.Url))
@@ -682,11 +495,10 @@ namespace Emby.Server.Implementations.HttpClientManager
/// Throws the cancellation exception.
/// </summary>
/// <param name="options">The options.</param>
/// <param name="client">The client.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="exception">The exception.</param>
/// <returns>Exception.</returns>
private Exception GetCancellationException(HttpRequestOptions options, HttpClientInfo client, CancellationToken cancellationToken, OperationCanceledException exception)
private Exception GetCancellationException(HttpRequestOptions options, CancellationToken cancellationToken, OperationCanceledException exception)
{
// If the HttpClient's timeout is reached, it will cancel the Task internally
if (!cancellationToken.IsCancellationRequested)
@@ -698,8 +510,6 @@ namespace Emby.Server.Implementations.HttpClientManager
_logger.LogError(msg);
}
client.LastTimeout = DateTime.UtcNow;
// Throw an HttpException so that the caller doesn't think it was cancelled by user code
return new HttpException(msg, exception)
{
@@ -710,91 +520,20 @@ namespace Emby.Server.Implementations.HttpClientManager
return exception;
}
private void EnsureSuccessStatusCode(HttpClientInfo client, HttpWebResponse response, HttpRequestOptions options)
private async Task EnsureSuccessStatusCode(HttpResponseMessage response, HttpRequestOptions options)
{
var statusCode = response.StatusCode;
var isSuccessful = statusCode >= HttpStatusCode.OK && statusCode <= (HttpStatusCode)299;
if (isSuccessful)
if (response.IsSuccessStatusCode)
{
return;
}
if (options.LogErrorResponseBody)
{
try
{
using (var stream = response.GetResponseStream())
{
if (stream != null)
{
using (var reader = new StreamReader(stream))
{
var msg = reader.ReadToEnd();
var msg = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
_logger.LogError("HTTP request failed with message: {Message}", msg);
_logger.LogError(msg);
}
}
}
}
catch
{
}
}
throw new HttpException(response.StatusDescription)
throw new HttpException(response.ReasonPhrase)
{
StatusCode = response.StatusCode
};
}
private static Task<WebResponse> GetResponseAsync(WebRequest request, TimeSpan timeout)
{
var taskCompletion = new TaskCompletionSource<WebResponse>();
var asyncTask = Task.Factory.FromAsync(request.BeginGetResponse, request.EndGetResponse, null);
ThreadPool.RegisterWaitForSingleObject((asyncTask as IAsyncResult).AsyncWaitHandle, TimeoutCallback, request, timeout, true);
var callback = new TaskCallback { taskCompletion = taskCompletion };
asyncTask.ContinueWith(callback.OnSuccess, TaskContinuationOptions.NotOnFaulted);
// Handle errors
asyncTask.ContinueWith(callback.OnError, TaskContinuationOptions.OnlyOnFaulted);
return taskCompletion.Task;
}
private static void TimeoutCallback(object state, bool timedOut)
{
if (timedOut && state != null)
{
var request = (WebRequest)state;
request.Abort();
}
}
private class TaskCallback
{
public TaskCompletionSource<WebResponse> taskCompletion;
public void OnSuccess(Task<WebResponse> task)
{
taskCompletion.TrySetResult(task.Result);
}
public void OnError(Task<WebResponse> task)
{
if (task.Exception == null)
{
taskCompletion.TrySetException(Enumerable.Empty<Exception>());
}
else
{
taskCompletion.TrySetException(task.Exception);
}
}
}
}
}

View File

@@ -71,7 +71,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
UserAgent = "Emby/3.0",
// Shouldn't matter but may cause issues
EnableHttpCompression = false
DecompressionMethod = CompressionMethod.None
};
using (var response = await _httpClient.SendAsync(httpRequestOptions, "GET").ConfigureAwait(false))

View File

@@ -96,8 +96,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
Url = ApiUrl + "/schedules",
UserAgent = UserAgent,
CancellationToken = cancellationToken,
// The data can be large so give it some extra time
TimeoutMs = 60000,
LogErrorResponseBody = true,
RequestContent = requestString
};
@@ -115,9 +113,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
Url = ApiUrl + "/programs",
UserAgent = UserAgent,
CancellationToken = cancellationToken,
LogErrorResponseBody = true,
// The data can be large so give it some extra time
TimeoutMs = 60000
LogErrorResponseBody = true
};
httpOptions.RequestHeaders["token"] = token;
@@ -483,8 +479,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
CancellationToken = cancellationToken,
RequestContent = imageIdString,
LogErrorResponseBody = true,
// The data can be large so give it some extra time
TimeoutMs = 60000
};
try
@@ -633,15 +627,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
ListingsProviderInfo providerInfo)
{
// Schedules direct requires that the client support compression and will return a 400 response without it
options.EnableHttpCompression = true;
// On windows 7 under .net core, this header is not getting added
#if NETSTANDARD2_0
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
{
options.RequestHeaders[HeaderNames.AcceptEncoding] = "deflate";
}
#endif
options.DecompressionMethod = CompressionMethod.Deflate;
try
{
@@ -671,15 +657,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
ListingsProviderInfo providerInfo)
{
// Schedules direct requires that the client support compression and will return a 400 response without it
options.EnableHttpCompression = true;
// On windows 7 under .net core, this header is not getting added
#if NETSTANDARD2_0
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
{
options.RequestHeaders[HeaderNames.AcceptEncoding] = "deflate";
}
#endif
options.DecompressionMethod = CompressionMethod.Deflate;
try
{
@@ -871,8 +849,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
UserAgent = UserAgent,
CancellationToken = cancellationToken,
LogErrorResponseBody = true,
// The data can be large so give it some extra time
TimeoutMs = 60000
};
httpOptions.RequestHeaders["token"] = token;

View File

@@ -2,14 +2,15 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Emby.XmlTv.Classes;
using Emby.XmlTv.Entities;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Dto;
@@ -27,7 +28,12 @@ namespace Emby.Server.Implementations.LiveTv.Listings
private readonly IFileSystem _fileSystem;
private readonly IZipClient _zipClient;
public XmlTvListingsProvider(IServerConfigurationManager config, IHttpClient httpClient, ILogger logger, IFileSystem fileSystem, IZipClient zipClient)
public XmlTvListingsProvider(
IServerConfigurationManager config,
IHttpClient httpClient,
ILogger logger,
IFileSystem fileSystem,
IZipClient zipClient)
{
_config = config;
_httpClient = httpClient;
@@ -52,7 +58,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
private async Task<string> GetXml(string path, CancellationToken cancellationToken)
{
_logger.LogInformation("xmltv path: {path}", path);
_logger.LogInformation("xmltv path: {Path}", path);
if (!path.StartsWith("http", StringComparison.OrdinalIgnoreCase))
{
@@ -66,26 +72,33 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return UnzipIfNeeded(path, cacheFile);
}
_logger.LogInformation("Downloading xmltv listings from {path}", path);
string tempFile = await _httpClient.GetTempFile(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = path,
Progress = new SimpleProgress<double>(),
DecompressionMethod = CompressionMethod.Gzip,
// It's going to come back gzipped regardless of this value
// So we need to make sure the decompression method is set to gzip
EnableHttpCompression = true,
UserAgent = "Emby/3.0"
}).ConfigureAwait(false);
_logger.LogInformation("Downloading xmltv listings from {Path}", path);
Directory.CreateDirectory(Path.GetDirectoryName(cacheFile));
File.Copy(tempFile, cacheFile, true);
using (var res = await _httpClient.SendAsync(
new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = path,
DecompressionMethod = CompressionMethod.Gzip,
},
HttpMethod.Get).ConfigureAwait(false))
using (var stream = res.Content)
using (var fileStream = new FileStream(cacheFile, FileMode.CreateNew))
{
if (res.ContentHeaders.ContentEncoding.Contains("gzip"))
{
using (var gzStream = new GZipStream(stream, CompressionMode.Decompress))
{
await gzStream.CopyToAsync(fileStream).ConfigureAwait(false);
}
}
else
{
await stream.CopyToAsync(fileStream).ConfigureAwait(false);
}
}
return UnzipIfNeeded(path, cacheFile);
}
@@ -103,7 +116,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
catch (Exception ex)
{
_logger.LogError(ex, "Error extracting from gz file {file}", file);
_logger.LogError(ex, "Error extracting from gz file {File}", file);
}
try
@@ -113,7 +126,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
catch (Exception ex)
{
_logger.LogError(ex, "Error extracting from zip file {file}", file);
_logger.LogError(ex, "Error extracting from zip file {File}", file);
}
}
@@ -161,20 +174,10 @@ namespace Emby.Server.Implementations.LiveTv.Listings
throw new ArgumentNullException(nameof(channelId));
}
/*
if (!await EmbyTV.EmbyTVRegistration.Instance.EnableXmlTv().ConfigureAwait(false))
{
var length = endDateUtc - startDateUtc;
if (length.TotalDays > 1)
{
endDateUtc = startDateUtc.AddDays(1);
}
}*/
_logger.LogDebug("Getting xmltv programs for channel {id}", channelId);
_logger.LogDebug("Getting xmltv programs for channel {Id}", channelId);
string path = await GetXml(info.Path, cancellationToken).ConfigureAwait(false);
_logger.LogDebug("Opening XmlTvReader for {path}", path);
_logger.LogDebug("Opening XmlTvReader for {Path}", path);
var reader = new XmlTvReader(path, GetLanguage(info));
return reader.GetProgrammes(channelId, startDateUtc, endDateUtc, cancellationToken)
@@ -267,7 +270,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
// In theory this should never be called because there is always only one lineup
string path = await GetXml(info.Path, CancellationToken.None).ConfigureAwait(false);
_logger.LogDebug("Opening XmlTvReader for {path}", path);
_logger.LogDebug("Opening XmlTvReader for {Path}", path);
var reader = new XmlTvReader(path, GetLanguage(info));
IEnumerable<XmlTvChannel> results = reader.GetChannels();
@@ -279,7 +282,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
// In theory this should never be called because there is always only one lineup
string path = await GetXml(info.Path, cancellationToken).ConfigureAwait(false);
_logger.LogDebug("Opening XmlTvReader for {path}", path);
_logger.LogDebug("Opening XmlTvReader for {Path}", path);
var reader = new XmlTvReader(path, GetLanguage(info));
var results = reader.GetChannels();

View File

@@ -138,7 +138,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
Url = string.Format("{0}/discover.json", GetApiUrl(info)),
CancellationToken = cancellationToken,
TimeoutMs = Convert.ToInt32(TimeSpan.FromSeconds(10).TotalMilliseconds),
BufferContent = false
}, "GET").ConfigureAwait(false))
@@ -191,7 +190,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
Url = string.Format("{0}/tuners.html", GetApiUrl(info)),
CancellationToken = cancellationToken,
TimeoutMs = Convert.ToInt32(TimeSpan.FromSeconds(5).TotalMilliseconds),
BufferContent = false
}))
{

View File

@@ -47,13 +47,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
CancellationToken = CancellationToken.None,
BufferContent = false,
// Increase a little bit
TimeoutMs = 30000,
EnableHttpCompression = false,
LogResponse = true,
LogResponseHeaders = true
DecompressionMethod = CompressionMethod.None,
};
foreach (var header in mediaSource.RequiredHttpHeaders)

View File

@@ -185,6 +185,11 @@ namespace Jellyfin.Drawing.Skia
public ImageDimensions GetImageSize(string path)
{
if (!File.Exists(path))
{
throw new FileNotFoundException("File not found", path);
}
using (var s = new SKFileStream(path))
using (var codec = SKCodec.Create(s))
{

View File

@@ -118,8 +118,20 @@ namespace Jellyfin.Server
SQLitePCL.Batteries_V2.Init();
// Increase the max http request limit
// The default connection limit is 10 for ASP.NET hosted applications and 2 for all others.
ServicePointManager.DefaultConnectionLimit = Math.Max(96, ServicePointManager.DefaultConnectionLimit);
// Disable the "Expect: 100-Continue" header by default
// http://stackoverflow.com/questions/566437/http-post-returns-the-error-417-expectation-failed-c
ServicePointManager.Expect100Continue = false;
// CA5359: Do Not Disable Certificate Validation
#pragma warning disable CA5359
// Allow all https requests
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; });
#pragma warning restore CA5359
var fileSystem = new ManagedFileSystem(_loggerFactory, appPaths);

View File

@@ -224,7 +224,17 @@ namespace MediaBrowser.Api.UserLibrary
request.IncludeItemTypes = "Playlist";
}
if (!(item is UserRootFolder) && !user.Policy.EnableAllFolders && !user.Policy.EnabledFolders.Any(i => new Guid(i) == item.Id))
bool isInEnabledFolder = user.Policy.EnabledFolders.Any(i => new Guid(i) == item.Id);
var collectionFolders = _libraryManager.GetCollectionFolders(item);
foreach (var collectionFolder in collectionFolders)
{
if (user.Policy.EnabledFolders.Contains(collectionFolder.Id.ToString("N"), StringComparer.OrdinalIgnoreCase))
{
isInEnabledFolder = true;
}
}
if (!(item is UserRootFolder) && !user.Policy.EnableAllFolders && !isInEnabledFolder)
{
Logger.LogWarning("{UserName} is not permitted to access Library {ItemName}.", user.Name, item.Name);
return new QueryResult<BaseItem>

View File

@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Microsoft.Net.Http.Headers;
@@ -17,7 +16,7 @@ namespace MediaBrowser.Common.Net
/// <value>The URL.</value>
public string Url { get; set; }
public CompressionMethod? DecompressionMethod { get; set; }
public CompressionMethod DecompressionMethod { get; set; }
/// <summary>
/// Gets or sets the accept header.
@@ -28,18 +27,13 @@ namespace MediaBrowser.Common.Net
get => GetHeaderValue(HeaderNames.Accept);
set => RequestHeaders[HeaderNames.Accept] = value;
}
/// <summary>
/// Gets or sets the cancellation token.
/// </summary>
/// <value>The cancellation token.</value>
public CancellationToken CancellationToken { get; set; }
/// <summary>
/// Gets or sets the resource pool.
/// </summary>
/// <value>The resource pool.</value>
public SemaphoreSlim ResourcePool { get; set; }
/// <summary>
/// Gets or sets the user agent.
/// </summary>
@@ -54,13 +48,21 @@ namespace MediaBrowser.Common.Net
/// Gets or sets the referrer.
/// </summary>
/// <value>The referrer.</value>
public string Referer { get; set; }
public string Referer
{
get => GetHeaderValue(HeaderNames.Referer);
set => RequestHeaders[HeaderNames.Referer] = value;
}
/// <summary>
/// Gets or sets the host.
/// </summary>
/// <value>The host.</value>
public string Host { get; set; }
public string Host
{
get => GetHeaderValue(HeaderNames.Host);
set => RequestHeaders[HeaderNames.Host] = value;
}
/// <summary>
/// Gets or sets the progress.
@@ -68,12 +70,6 @@ namespace MediaBrowser.Common.Net
/// <value>The progress.</value>
public IProgress<double> Progress { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [enable HTTP compression].
/// </summary>
/// <value><c>true</c> if [enable HTTP compression]; otherwise, <c>false</c>.</value>
public bool EnableHttpCompression { get; set; }
public Dictionary<string, string> RequestHeaders { get; private set; }
public string RequestContentType { get; set; }
@@ -86,8 +82,6 @@ namespace MediaBrowser.Common.Net
public bool LogRequest { get; set; }
public bool LogRequestAsDebug { get; set; }
public bool LogErrors { get; set; }
public bool LogResponse { get; set; }
public bool LogResponseHeaders { get; set; }
public bool LogErrorResponseBody { get; set; }
public bool EnableKeepAlive { get; set; }
@@ -95,12 +89,8 @@ namespace MediaBrowser.Common.Net
public CacheMode CacheMode { get; set; }
public TimeSpan CacheLength { get; set; }
public int TimeoutMs { get; set; }
public bool EnableDefaultUserAgent { get; set; }
public bool AppendCharsetToMimeType { get; set; }
public string DownloadFilePath { get; set; }
private string GetHeaderValue(string name)
{
RequestHeaders.TryGetValue(name, out var value);
@@ -113,24 +103,12 @@ namespace MediaBrowser.Common.Net
/// </summary>
public HttpRequestOptions()
{
EnableHttpCompression = true;
RequestHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
LogRequest = true;
LogErrors = true;
CacheMode = CacheMode.None;
TimeoutMs = 20000;
}
public void SetPostData(IDictionary<string, string> values)
{
var strings = values.Keys.Select(key => string.Format("{0}={1}", key, values[key]));
var postContent = string.Join("&", strings.ToArray());
RequestContent = postContent;
RequestContentType = "application/x-www-form-urlencoded";
DecompressionMethod = CompressionMethod.Deflate;
}
}
@@ -140,9 +118,11 @@ namespace MediaBrowser.Common.Net
Unconditional = 1
}
[Flags]
public enum CompressionMethod
{
Deflate,
Gzip
None = 0b00000001,
Deflate = 0b00000010,
Gzip = 0b00000100
}
}

View File

@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http.Headers;
namespace MediaBrowser.Common.Net
{
@@ -50,26 +50,28 @@ namespace MediaBrowser.Common.Net
/// Gets or sets the headers.
/// </summary>
/// <value>The headers.</value>
public Dictionary<string, string> Headers { get; set; }
public HttpResponseHeaders Headers { get; set; }
private readonly IDisposable _disposable;
/// <summary>
/// Gets or sets the content headers.
/// </summary>
/// <value>The content headers.</value>
public HttpContentHeaders ContentHeaders { get; set; }
public HttpResponseInfo(IDisposable disposable)
{
_disposable = disposable;
Headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
public HttpResponseInfo()
{
Headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
public HttpResponseInfo(HttpResponseHeaders headers, HttpContentHeaders contentHeader)
{
Headers = headers;
ContentHeaders = contentHeader;
}
public void Dispose()
{
if (_disposable != null)
{
_disposable.Dispose();
}
// Only IDisposable for backwards compatibility
}
}
}

View File

@@ -1,5 +1,6 @@
using System.IO;
using System.Threading.Tasks;
using System.Net.Http;
namespace MediaBrowser.Common.Net
{
@@ -23,6 +24,8 @@ namespace MediaBrowser.Common.Net
Task<Stream> Get(HttpRequestOptions options);
/// <summary>
/// Warning: Deprecated function,
/// use 'Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, HttpMethod httpMethod);' instead
/// Sends the asynchronous.
/// </summary>
/// <param name="options">The options.</param>
@@ -30,6 +33,14 @@ namespace MediaBrowser.Common.Net
/// <returns>Task{HttpResponseInfo}.</returns>
Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, string httpMethod);
/// <summary>
/// Sends the asynchronous.
/// </summary>
/// <param name="options">The options.</param>
/// <param name="httpMethod">The HTTP method.</param>
/// <returns>Task{HttpResponseInfo}.</returns>
Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, HttpMethod httpMethod);
/// <summary>
/// Posts the specified options.
/// </summary>

View File

@@ -24,24 +24,28 @@ namespace MediaBrowser.Providers.TV.TheTVDB
{
_cache = memoryCache;
_tvDbClient = new TvDbClient();
_tvDbClient.Authentication.AuthenticateAsync(TvdbUtils.TvdbApiKey);
_tokenCreatedAt = DateTime.Now;
}
public TvDbClient TvDbClient
private TvDbClient TvDbClient
{
get
{
if (string.IsNullOrEmpty(_tvDbClient.Authentication.Token))
{
_tvDbClient.Authentication.AuthenticateAsync(TvdbUtils.TvdbApiKey).GetAwaiter().GetResult();
_tokenCreatedAt = DateTime.Now;
}
// Refresh if necessary
if (_tokenCreatedAt < DateTime.Now.Subtract(TimeSpan.FromHours(20)))
{
try
{
_tvDbClient.Authentication.RefreshTokenAsync();
_tvDbClient.Authentication.RefreshTokenAsync().GetAwaiter().GetResult();
}
catch
{
_tvDbClient.Authentication.AuthenticateAsync(TvdbUtils.TvdbApiKey);
_tvDbClient.Authentication.AuthenticateAsync(TvdbUtils.TvdbApiKey).GetAwaiter().GetResult();
}
_tokenCreatedAt = DateTime.Now;

View File

@@ -57,10 +57,9 @@ namespace Mono.Nat.Upnp
req.Url = ss;
req.EnableKeepAlive = false;
req.RequestContentType = "text/xml";
req.AppendCharsetToMimeType = true;
req.RequestHeaders.Add("SOAPACTION", "\"" + device.ServiceType + "#" + upnpMethod + "\"");
string bodyString = "<s:Envelope "
req.RequestContent = "<s:Envelope "
+ "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+ "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
+ "<s:Body>"
@@ -70,8 +69,6 @@ namespace Mono.Nat.Upnp
+ "</u:" + upnpMethod + ">"
+ "</s:Body>"
+ "</s:Envelope>\r\n\r\n";
req.RequestContentBytes = System.Text.Encoding.UTF8.GetBytes(bodyString);
return req;
}

View File

@@ -1,4 +1,4 @@
using System.Reflection;
[assembly: AssemblyVersion("10.3.5")]
[assembly: AssemblyFileVersion("10.3.5")]
[assembly: AssemblyVersion("10.3.7")]
[assembly: AssemblyFileVersion("10.3.7")]

View File

@@ -1,7 +1,7 @@
---
# We just wrap `build` so this is really it
name: "jellyfin"
version: "10.3.5"
version: "10.3.7"
packages:
- debian-package-x64
- debian-package-armhf

View File

@@ -1,20 +1,36 @@
#!/bin/bash
NAME=jellyfin
# restart.sh - Jellyfin server restart script
# Part of the Jellyfin project (https://github.com/jellyfin)
#
# This script restarts the Jellyfin daemon on Linux when using
# the Restart button on the admin dashboard. It supports the
# systemctl, service, and traditional /etc/init.d (sysv) restart
# methods, chosen automatically by which one is found first (in
# that order).
#
# This script is used by the Debian/Ubuntu/Fedora/CentOS packages.
restart_cmds=(
"systemctl restart ${NAME}"
"service ${NAME} restart"
"/etc/init.d/${NAME} restart"
"s6-svc -t /var/run/s6/services/${NAME}"
)
get_service_command() {
for command in systemctl service; do
if which $command &>/dev/null; then
echo $command && return
fi
done
echo "sysv"
}
for restart_cmd in "${restart_cmds[@]}"; do
cmd=$(echo "$restart_cmd" | awk '{print $1}')
cmd_loc=$(command -v ${cmd})
if [[ -n "$cmd_loc" ]]; then
restart_cmd=$(echo "$restart_cmd" | sed -e "s%${cmd}%${cmd_loc}%")
echo "sleep 2; sudo $restart_cmd > /dev/null 2>&1" | at now > /dev/null 2>&1
exit 0
fi
done
cmd="$( get_service_command )"
echo "Detected service control platform '$cmd'; using it to restart Jellyfin..."
case $cmd in
'systemctl')
echo "sleep 2; /usr/bin/sudo $( which systemctl ) restart jellyfin" | at now
;;
'service')
echo "sleep 2; /usr/bin/sudo $( which service ) jellyfin restart" | at now
;;
'sysv')
echo "sleep 2; /usr/bin/sudo /etc/init.d/jellyfin restart" | at now
;;
esac
exit 0

View File

@@ -1,3 +1,15 @@
jellyfin (10.3.7-1) unstable; urgency=medium
* New upstream version 10.3.7; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.7
-- Jellyfin Packaging Team <packaging@jellyfin.org> Wed, 24 Jul 2019 10:48:28 -0400
jellyfin (10.3.6-1) unstable; urgency=medium
* New upstream version 10.3.6; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.6
-- Jellyfin Packaging Team <packaging@jellyfin.org> Sat, 06 Jul 2019 13:34:19 -0400
jellyfin (10.3.5-1) unstable; urgency=medium
* New upstream version 10.3.5; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.5

View File

@@ -7,7 +7,7 @@
%endif
Name: jellyfin
Version: 10.3.5
Version: 10.3.7
Release: 1%{?dist}
Summary: The Free Software Media Browser
License: GPLv2
@@ -140,6 +140,10 @@ fi
%systemd_postun_with_restart jellyfin.service
%changelog
* Wed Jul 24 2019 Jellyfin Packaging Team <packaging@jellyfin.org>
- New upstream version 10.3.7; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.7
* Sat Jul 06 2019 Jellyfin Packaging Team <packaging@jellyfin.org>
- New upstream version 10.3.6; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.6
* Sun Jun 09 2019 Jellyfin Packaging Team <packaging@jellyfin.org>
- New upstream version 10.3.5; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.5
* Thu Jun 06 2019 Jellyfin Packaging Team <packaging@jellyfin.org>

View File

@@ -1,6 +1,36 @@
#!/bin/sh
#!/bin/bash
NAME=jellyfin
restart_cmd="/usr/bin/systemctl restart ${NAME}"
echo "sleep 2; sudo $restart_cmd > /dev/null 2>&1" | at now > /dev/null 2>&1
exit 0
# restart.sh - Jellyfin server restart script
# Part of the Jellyfin project (https://github.com/jellyfin)
#
# This script restarts the Jellyfin daemon on Linux when using
# the Restart button on the admin dashboard. It supports the
# systemctl, service, and traditional /etc/init.d (sysv) restart
# methods, chosen automatically by which one is found first (in
# that order).
#
# This script is used by the Debian/Ubuntu/Fedora/CentOS packages.
get_service_command() {
for command in systemctl service; do
if which $command &>/dev/null; then
echo $command && return
fi
done
echo "sysv"
}
cmd="$( get_service_command )"
echo "Detected service control platform '$cmd'; using it to restart Jellyfin..."
case $cmd in
'systemctl')
echo "sleep 2; /usr/bin/sudo $( which systemctl ) restart jellyfin" | at now
;;
'service')
echo "sleep 2; /usr/bin/sudo $( which service ) jellyfin restart" | at now
;;
'sysv')
echo "sleep 2; /usr/bin/sudo /etc/init.d/jellyfin restart" | at now
;;
esac
exit 0