diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 486221bc6..966d48174 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -260,20 +260,10 @@ namespace MediaBrowser.Api.Playback
{
var quality = ServerConfigurationManager.Configuration.EncodingQuality;
- if (quality == EncodingQuality.Auto)
- {
- var cpuCount = Environment.ProcessorCount;
-
- if (cpuCount >= 4)
- {
- return 0;
- }
-
- return cpuCount;
- }
-
switch (quality)
{
+ case EncodingQuality.Auto:
+ return 0;
case EncodingQuality.HighSpeed:
return 2;
case EncodingQuality.HighQuality:
diff --git a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
index b5317319f..07c0f8ab7 100644
--- a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
+++ b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
@@ -132,7 +132,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
#if __MonoCS__
return GetMonoRequest(options, method, enableHttpCompression);
#endif
-
+
var request = HttpWebRequest.CreateHttp(options.Url);
if (!string.IsNullOrEmpty(options.AcceptHeader))
@@ -172,9 +172,64 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
/// Task{HttpResponseInfo}.
///
///
- public async Task GetResponse(HttpRequestOptions options)
+ public Task GetResponse(HttpRequestOptions options)
{
- ValidateParams(options.Url, options.CancellationToken);
+ return SendAsync(options, "GET");
+ }
+
+ ///
+ /// Performs a GET request and returns the resulting stream
+ ///
+ /// The options.
+ /// Task{Stream}.
+ ///
+ ///
+ public async Task Get(HttpRequestOptions options)
+ {
+ var response = await GetResponse(options).ConfigureAwait(false);
+
+ return response.Content;
+ }
+
+ ///
+ /// Performs a GET request and returns the resulting stream
+ ///
+ /// The URL.
+ /// The resource pool.
+ /// The cancellation token.
+ /// Task{Stream}.
+ public Task Get(string url, SemaphoreSlim resourcePool, CancellationToken cancellationToken)
+ {
+ return Get(new HttpRequestOptions
+ {
+ Url = url,
+ ResourcePool = resourcePool,
+ CancellationToken = cancellationToken,
+ });
+ }
+
+ ///
+ /// Gets the specified URL.
+ ///
+ /// The URL.
+ /// The cancellation token.
+ /// Task{Stream}.
+ public Task Get(string url, CancellationToken cancellationToken)
+ {
+ return Get(url, null, cancellationToken);
+ }
+
+ ///
+ /// send as an asynchronous operation.
+ ///
+ /// The options.
+ /// The HTTP method.
+ /// Task{HttpResponseInfo}.
+ ///
+ ///
+ private async Task SendAsync(HttpRequestOptions options, string httpMethod)
+ {
+ ValidateParams(options);
options.CancellationToken.ThrowIfCancellationRequested();
@@ -185,7 +240,17 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
throw new HttpException(string.Format("Cancelling connection to {0} due to a previous timeout.", options.Url)) { IsTimedOut = true };
}
- var httpWebRequest = GetRequest(options, "GET", options.EnableHttpCompression);
+ var httpWebRequest = GetRequest(options, httpMethod, options.EnableHttpCompression);
+
+ if (!string.IsNullOrEmpty(options.RequestContent) || string.Equals(httpMethod, "post", StringComparison.OrdinalIgnoreCase))
+ {
+ var content = options.RequestContent ?? string.Empty;
+ var bytes = Encoding.UTF8.GetBytes(content);
+
+ httpWebRequest.ContentType = options.RequestContentType ?? "application/x-www-form-urlencoded";
+ httpWebRequest.ContentLength = bytes.Length;
+ httpWebRequest.GetRequestStream().Write(bytes, 0, bytes.Length);
+ }
if (options.ResourcePool != null)
{
@@ -202,7 +267,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
throw new HttpException(string.Format("Connection to {0} timed out", options.Url)) { IsTimedOut = true };
}
- _logger.Info("HttpClientManager.GET url: {0}", options.Url);
+ _logger.Info("HttpClientManager {0}: {1}", httpMethod.ToUpper(), options.Url);
try
{
@@ -275,46 +340,9 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
}
}
- ///
- /// Performs a GET request and returns the resulting stream
- ///
- /// The options.
- /// Task{Stream}.
- ///
- ///
- public async Task Get(HttpRequestOptions options)
+ public Task Post(HttpRequestOptions options)
{
- var response = await GetResponse(options).ConfigureAwait(false);
-
- return response.Content;
- }
-
- ///
- /// Performs a GET request and returns the resulting stream
- ///
- /// The URL.
- /// The resource pool.
- /// The cancellation token.
- /// Task{Stream}.
- public Task Get(string url, SemaphoreSlim resourcePool, CancellationToken cancellationToken)
- {
- return Get(new HttpRequestOptions
- {
- Url = url,
- ResourcePool = resourcePool,
- CancellationToken = cancellationToken,
- });
- }
-
- ///
- /// Gets the specified URL.
- ///
- /// The URL.
- /// The cancellation token.
- /// Task{Stream}.
- public Task Get(string url, CancellationToken cancellationToken)
- {
- return Get(url, null, cancellationToken);
+ return SendAsync(options, "POST");
}
///
@@ -329,82 +357,15 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
///
public async Task Post(HttpRequestOptions options, Dictionary postData)
{
- ValidateParams(options.Url, options.CancellationToken);
-
- options.CancellationToken.ThrowIfCancellationRequested();
-
- var httpWebRequest = GetRequest(options, "POST", options.EnableHttpCompression);
-
var strings = postData.Keys.Select(key => string.Format("{0}={1}", key, postData[key]));
var postContent = string.Join("&", strings.ToArray());
- var bytes = Encoding.UTF8.GetBytes(postContent);
- httpWebRequest.ContentType = "application/x-www-form-urlencoded";
- httpWebRequest.ContentLength = bytes.Length;
- httpWebRequest.GetRequestStream().Write(bytes, 0, bytes.Length);
+ options.RequestContent = postContent;
+ options.RequestContentType = "application/x-www-form-urlencoded";
- if (options.ResourcePool != null)
- {
- await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false);
- }
+ var response = await Post(options).ConfigureAwait(false);
- _logger.Info("HttpClientManager.POST url: {0}", options.Url);
-
- try
- {
- options.CancellationToken.ThrowIfCancellationRequested();
-
- using (var response = await httpWebRequest.GetResponseAsync().ConfigureAwait(false))
- {
- var httpResponse = (HttpWebResponse)response;
-
- EnsureSuccessStatusCode(httpResponse);
-
- options.CancellationToken.ThrowIfCancellationRequested();
-
- using (var stream = httpResponse.GetResponseStream())
- {
- var memoryStream = new MemoryStream();
-
- await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
-
- memoryStream.Position = 0;
-
- return memoryStream;
- }
- }
- }
- catch (OperationCanceledException ex)
- {
- var exception = GetCancellationException(options.Url, options.CancellationToken, ex);
-
- throw exception;
- }
- catch (HttpRequestException ex)
- {
- _logger.ErrorException("Error getting response from " + options.Url, ex);
-
- throw new HttpException(ex.Message, ex);
- }
- catch (WebException ex)
- {
- _logger.ErrorException("Error getting response from " + options.Url, ex);
-
- throw new HttpException(ex.Message, ex);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting response from " + options.Url, ex);
-
- throw;
- }
- finally
- {
- if (options.ResourcePool != null)
- {
- options.ResourcePool.Release();
- }
- }
+ return response.Content;
}
///
@@ -443,7 +404,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
public async Task GetTempFileResponse(HttpRequestOptions options)
{
- ValidateParams(options.Url, options.CancellationToken);
+ ValidateParams(options);
Directory.CreateDirectory(_appPaths.TempDirectory);
@@ -592,7 +553,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
{
return new HttpException(ex.Message, ex);
}
-
+
return ex;
}
@@ -608,17 +569,11 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
}
}
- ///
- /// Validates the params.
- ///
- /// The URL.
- /// The cancellation token.
- /// url
- private void ValidateParams(string url, CancellationToken cancellationToken)
+ private void ValidateParams(HttpRequestOptions options)
{
- if (string.IsNullOrEmpty(url))
+ if (string.IsNullOrEmpty(options.Url))
{
- throw new ArgumentNullException("url");
+ throw new ArgumentNullException("options");
}
}
diff --git a/MediaBrowser.Common/Net/HttpRequestOptions.cs b/MediaBrowser.Common/Net/HttpRequestOptions.cs
index 977a6aabe..db78fc927 100644
--- a/MediaBrowser.Common/Net/HttpRequestOptions.cs
+++ b/MediaBrowser.Common/Net/HttpRequestOptions.cs
@@ -66,6 +66,10 @@ namespace MediaBrowser.Common.Net
public Dictionary RequestHeaders { get; private set; }
+ public string RequestContentType { get; set; }
+
+ public string RequestContent { get; set; }
+
private string GetHeaderValue(string name)
{
string value;
diff --git a/MediaBrowser.Common/Net/IHttpClient.cs b/MediaBrowser.Common/Net/IHttpClient.cs
index 54d6665e2..a3d90cb0d 100644
--- a/MediaBrowser.Common/Net/IHttpClient.cs
+++ b/MediaBrowser.Common/Net/IHttpClient.cs
@@ -64,6 +64,13 @@ namespace MediaBrowser.Common.Net
/// Task{Stream}.
Task Post(string url, Dictionary postData, CancellationToken cancellationToken);
+ ///
+ /// Posts the specified options.
+ ///
+ /// The options.
+ /// Task{HttpResponseInfo}.
+ Task Post(HttpRequestOptions options);
+
///
/// Downloads the contents of a given url into a temporary location
///
diff --git a/MediaBrowser.Controller/LiveTv/ProgramInfo.cs b/MediaBrowser.Controller/LiveTv/ProgramInfo.cs
index ce7a4a598..5cf8fb721 100644
--- a/MediaBrowser.Controller/LiveTv/ProgramInfo.cs
+++ b/MediaBrowser.Controller/LiveTv/ProgramInfo.cs
@@ -17,6 +17,12 @@ namespace MediaBrowser.Controller.LiveTv
/// The channel identifier.
public string ChannelId { get; set; }
+ ///
+ /// Gets or sets the name of the channel.
+ ///
+ /// The name of the channel.
+ public string ChannelName { get; set; }
+
///
/// Name of the program
///
diff --git a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs b/MediaBrowser.Controller/LiveTv/RecordingInfo.cs
index 4fc8c0f7a..40a53e659 100644
--- a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs
+++ b/MediaBrowser.Controller/LiveTv/RecordingInfo.cs
@@ -38,6 +38,12 @@ namespace MediaBrowser.Controller.LiveTv
/// The path.
public string Path { get; set; }
+ ///
+ /// Gets or sets the URL.
+ ///
+ /// The URL.
+ public string Url { get; set; }
+
///
/// Gets or sets the overview.
///
diff --git a/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs b/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs
index 607282796..b92ec4a43 100644
--- a/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs
+++ b/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs
@@ -48,11 +48,23 @@ namespace MediaBrowser.Controller.LiveTv
public DateTime EndDate { get; set; }
///
- /// Gets or sets the type of the recurrence.
+ /// Gets or sets a value indicating whether [record any time].
///
- /// The type of the recurrence.
- public RecurrenceType RecurrenceType { get; set; }
+ /// true if [record any time]; otherwise, false.
+ public bool RecordAnyTime { get; set; }
+ ///
+ /// Gets or sets a value indicating whether [record any channel].
+ ///
+ /// true if [record any channel]; otherwise, false.
+ public bool RecordAnyChannel { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether [record new only].
+ ///
+ /// true if [record new only]; otherwise, false.
+ public bool RecordNewOnly { get; set; }
+
///
/// Gets or sets the days.
///
diff --git a/MediaBrowser.Model/LiveTv/ProgramInfoDto.cs b/MediaBrowser.Model/LiveTv/ProgramInfoDto.cs
index b3542fcf8..106b78550 100644
--- a/MediaBrowser.Model/LiveTv/ProgramInfoDto.cs
+++ b/MediaBrowser.Model/LiveTv/ProgramInfoDto.cs
@@ -23,6 +23,12 @@ namespace MediaBrowser.Model.LiveTv
/// The channel identifier.
public string ChannelId { get; set; }
+ ///
+ /// Gets or sets the name of the channel.
+ ///
+ /// The name of the channel.
+ public string ChannelName { get; set; }
+
///
/// Gets or sets the community rating.
///
diff --git a/MediaBrowser.Model/LiveTv/RecordingInfoDto.cs b/MediaBrowser.Model/LiveTv/RecordingInfoDto.cs
index 501e3b621..2f836dc4d 100644
--- a/MediaBrowser.Model/LiveTv/RecordingInfoDto.cs
+++ b/MediaBrowser.Model/LiveTv/RecordingInfoDto.cs
@@ -50,6 +50,12 @@ namespace MediaBrowser.Model.LiveTv
/// The path.
public string Path { get; set; }
+ ///
+ /// Gets or sets the URL.
+ ///
+ /// The URL.
+ public string Url { get; set; }
+
///
/// Overview of the recording.
///
diff --git a/MediaBrowser.Model/LiveTv/RecordingStatus.cs b/MediaBrowser.Model/LiveTv/RecordingStatus.cs
index 06bc98e63..95e9dcb01 100644
--- a/MediaBrowser.Model/LiveTv/RecordingStatus.cs
+++ b/MediaBrowser.Model/LiveTv/RecordingStatus.cs
@@ -14,15 +14,6 @@ namespace MediaBrowser.Model.LiveTv
Error
}
- public enum RecurrenceType
- {
- Manual,
- NewProgramEventsOneChannel,
- AllProgramEventsOneChannel,
- NewProgramEventsAllChannels,
- AllProgramEventsAllChannels
- }
-
public enum DayPattern
{
Daily,
diff --git a/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs b/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs
index 085340013..fd61b8722 100644
--- a/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs
+++ b/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs
@@ -71,10 +71,22 @@ namespace MediaBrowser.Model.LiveTv
public DateTime EndDate { get; set; }
///
- /// Gets or sets the type of the recurrence.
+ /// Gets or sets a value indicating whether [record any time].
///
- /// The type of the recurrence.
- public RecurrenceType RecurrenceType { get; set; }
+ /// true if [record any time]; otherwise, false.
+ public bool RecordAnyTime { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether [record any channel].
+ ///
+ /// true if [record any channel]; otherwise, false.
+ public bool RecordAnyChannel { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether [record new only].
+ ///
+ /// true if [record new only]; otherwise, false.
+ public bool RecordNewOnly { get; set; }
///
/// Gets or sets the days.
diff --git a/MediaBrowser.Model/LiveTv/TimerInfoDto.cs b/MediaBrowser.Model/LiveTv/TimerInfoDto.cs
index d962f784f..b19099c4c 100644
--- a/MediaBrowser.Model/LiveTv/TimerInfoDto.cs
+++ b/MediaBrowser.Model/LiveTv/TimerInfoDto.cs
@@ -43,6 +43,12 @@ namespace MediaBrowser.Model.LiveTv
/// The program identifier.
public string ProgramId { get; set; }
+ ///
+ /// Gets or sets the external program identifier.
+ ///
+ /// The external program identifier.
+ public string ExternalProgramId { get; set; }
+
///
/// Name of the recording.
///
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs
index fc030dbf8..a30d1cfbb 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs
@@ -47,7 +47,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
RequiredPrePaddingSeconds = info.RequiredPrePaddingSeconds,
ExternalChannelId = info.ChannelId,
ExternalSeriesTimerId = info.SeriesTimerId,
- ServiceName = service.Name
+ ServiceName = service.Name,
+ ExternalProgramId = info.ProgramId
};
var duration = info.EndDate - info.StartDate;
@@ -78,7 +79,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv
RequiredPrePaddingSeconds = info.RequiredPrePaddingSeconds,
Days = info.Days,
Priority = info.Priority,
- RecurrenceType = info.RecurrenceType,
+ RecordAnyChannel = info.RecordAnyChannel,
+ RecordAnyTime = info.RecordAnyTime,
+ RecordNewOnly = info.RecordNewOnly,
ExternalChannelId = info.ChannelId,
ExternalProgramId = info.ProgramId,
ServiceName = service.Name
@@ -146,7 +149,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
OfficialRating = info.OfficialRating,
Audio = info.Audio,
IsHD = info.IsHD,
- ServiceName = service.Name
+ ServiceName = service.Name,
+ Url = info.Url
};
if (user != null)
@@ -219,7 +223,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
CommunityRating = program.CommunityRating,
AspectRatio = program.AspectRatio,
IsRepeat = program.IsRepeat,
- EpisodeTitle = program.EpisodeTitle
+ EpisodeTitle = program.EpisodeTitle,
+ ChannelName = program.ChannelName
};
if (user != null)
@@ -302,7 +307,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
RequestedPostPaddingSeconds = dto.RequestedPostPaddingSeconds,
RequestedPrePaddingSeconds = dto.RequestedPrePaddingSeconds,
RequiredPostPaddingSeconds = dto.RequiredPostPaddingSeconds,
- RequiredPrePaddingSeconds = dto.RequiredPrePaddingSeconds
+ RequiredPrePaddingSeconds = dto.RequiredPrePaddingSeconds,
+ ProgramId = dto.ExternalProgramId
};
}
@@ -323,8 +329,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv
RequiredPrePaddingSeconds = dto.RequiredPrePaddingSeconds,
Days = dto.Days,
Priority = dto.Priority,
- RecurrenceType = dto.RecurrenceType,
- ProgramId = dto.ExternalProgramId
+ ProgramId = dto.ExternalProgramId,
+ RecordAnyChannel = dto.RecordAnyChannel,
+ RecordAnyTime = dto.RecordAnyTime,
+ RecordNewOnly = dto.RecordNewOnly
};
}
}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
index e06b99999..b56f94a36 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -336,7 +336,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
.ToList();
}
- var returnArray = list.OrderByDescending(i => i.StartDate)
+ var returnArray = list.OrderBy(i => i.StartDate)
.ToArray();
return new QueryResult
diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs
index 4e44f156a..8088d35b7 100644
--- a/MediaBrowser.WebDashboard/Api/DashboardService.cs
+++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs
@@ -275,7 +275,9 @@ namespace MediaBrowser.WebDashboard.Api
// Don't cache if not configured to do so
// But always cache images to simulate production
- if (!_serverConfigurationManager.Configuration.EnableDashboardResponseCaching && !contentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase))
+ if (!_serverConfigurationManager.Configuration.EnableDashboardResponseCaching &&
+ !contentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase) &&
+ !contentType.StartsWith("font/", StringComparison.OrdinalIgnoreCase))
{
return ResultFactory.GetResult(GetResourceStream(path).Result, contentType);
}
@@ -284,7 +286,7 @@ namespace MediaBrowser.WebDashboard.Api
// Cache images unconditionally - updates to image files will require new filename
// If there's a version number in the query string we can cache this unconditionally
- if (contentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase) || !string.IsNullOrEmpty(request.V))
+ if (contentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase) || contentType.StartsWith("font/", StringComparison.OrdinalIgnoreCase) || !string.IsNullOrEmpty(request.V))
{
cacheDuration = TimeSpan.FromDays(365);
}
diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec
index d453d6757..7878753f2 100644
--- a/Nuget/MediaBrowser.Common.Internal.nuspec
+++ b/Nuget/MediaBrowser.Common.Internal.nuspec
@@ -2,7 +2,7 @@
MediaBrowser.Common.Internal
- 3.0.273
+ 3.0.275
MediaBrowser.Common.Internal
Luke
ebr,Luke,scottisafool
@@ -12,7 +12,7 @@
Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.
Copyright © Media Browser 2013
-
+
diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec
index 16ebaa8a8..34133bf6e 100644
--- a/Nuget/MediaBrowser.Common.nuspec
+++ b/Nuget/MediaBrowser.Common.nuspec
@@ -2,7 +2,7 @@
MediaBrowser.Common
- 3.0.273
+ 3.0.275
MediaBrowser.Common
Media Browser Team
ebr,Luke,scottisafool
diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec
index 33470aa62..92bd239e4 100644
--- a/Nuget/MediaBrowser.Server.Core.nuspec
+++ b/Nuget/MediaBrowser.Server.Core.nuspec
@@ -2,7 +2,7 @@
MediaBrowser.Server.Core
- 3.0.273
+ 3.0.275
Media Browser.Server.Core
Media Browser Team
ebr,Luke,scottisafool
@@ -12,7 +12,7 @@
Contains core components required to build plugins for Media Browser Server.
Copyright © Media Browser 2013
-
+