Merge with default

This commit is contained in:
ebr11 Eric Reed spam 2012-09-20 11:28:02 -04:00
commit e2ae376eef
36 changed files with 622 additions and 707 deletions

View File

@ -170,7 +170,7 @@ namespace MediaBrowser.Api
dto.TrailerUrl = item.TrailerUrl;
dto.Type = item.GetType().Name;
dto.UserRating = item.UserRating;
dto.CommunityRating = item.CommunityRating;
dto.UserData = GetDtoUserItemData(item.GetUserData(user, false));

View File

@ -90,14 +90,15 @@ namespace MediaBrowser.Api.HttpHandlers
}
}
public override Task<string> GetContentType()
protected override Task<ResponseInfo> GetResponseInfo()
{
return Task.FromResult(MimeTypes.GetMimeType("." + GetConversionOutputFormat()));
}
ResponseInfo info = new ResponseInfo
{
ContentType = MimeTypes.GetMimeType("." + GetConversionOutputFormat()),
CompressResponse = false
};
public override bool ShouldCompressResponse(string contentType)
{
return false;
return Task.FromResult<ResponseInfo>(info);
}
public override Task ProcessRequest(HttpListenerContext ctx)

View File

@ -1,13 +1,12 @@
using MediaBrowser.Common.Logging;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Net.Handlers;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Entities;
using System;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
@ -20,8 +19,9 @@ namespace MediaBrowser.Api.HttpHandlers
{
return ApiService.IsApiUrlMatch("image", request);
}
private string _imagePath;
private async Task<string> GetImagePath()
{
_imagePath = _imagePath ?? await DiscoverImagePath();
@ -29,121 +29,101 @@ namespace MediaBrowser.Api.HttpHandlers
return _imagePath;
}
private BaseEntity _sourceEntity;
private async Task<BaseEntity> GetSourceEntity()
{
if (_sourceEntity == null)
{
if (!string.IsNullOrEmpty(QueryString["personname"]))
{
_sourceEntity =
await Kernel.Instance.ItemController.GetPerson(QueryString["personname"]).ConfigureAwait(false);
}
else if (!string.IsNullOrEmpty(QueryString["genre"]))
{
_sourceEntity =
await Kernel.Instance.ItemController.GetGenre(QueryString["genre"]).ConfigureAwait(false);
}
else if (!string.IsNullOrEmpty(QueryString["year"]))
{
_sourceEntity =
await
Kernel.Instance.ItemController.GetYear(int.Parse(QueryString["year"])).ConfigureAwait(false);
}
else if (!string.IsNullOrEmpty(QueryString["studio"]))
{
_sourceEntity =
await Kernel.Instance.ItemController.GetStudio(QueryString["studio"]).ConfigureAwait(false);
}
else if (!string.IsNullOrEmpty(QueryString["userid"]))
{
_sourceEntity = ApiService.GetUserById(QueryString["userid"], false);
}
else
{
_sourceEntity = ApiService.GetItemById(QueryString["id"]);
}
}
return _sourceEntity;
}
private async Task<string> DiscoverImagePath()
{
string personName = QueryString["personname"];
var entity = await GetSourceEntity().ConfigureAwait(false);
if (!string.IsNullOrEmpty(personName))
{
return (await Kernel.Instance.ItemController.GetPerson(personName).ConfigureAwait(false)).PrimaryImagePath;
}
string genreName = QueryString["genre"];
if (!string.IsNullOrEmpty(genreName))
{
return (await Kernel.Instance.ItemController.GetGenre(genreName).ConfigureAwait(false)).PrimaryImagePath;
}
string year = QueryString["year"];
if (!string.IsNullOrEmpty(year))
{
return (await Kernel.Instance.ItemController.GetYear(int.Parse(year)).ConfigureAwait(false)).PrimaryImagePath;
}
string studio = QueryString["studio"];
if (!string.IsNullOrEmpty(studio))
{
return (await Kernel.Instance.ItemController.GetStudio(studio).ConfigureAwait(false)).PrimaryImagePath;
}
string userId = QueryString["userid"];
if (!string.IsNullOrEmpty(userId))
{
return ApiService.GetUserById(userId, false).PrimaryImagePath;
}
BaseItem item = ApiService.GetItemById(QueryString["id"]);
string imageIndex = QueryString["index"];
int index = string.IsNullOrEmpty(imageIndex) ? 0 : int.Parse(imageIndex);
return GetImagePathFromTypes(item, ImageType, index);
return ImageProcessor.GetImagePath(entity, ImageType, ImageIndex);
}
private Stream _sourceStream;
private async Task<Stream> GetSourceStream()
protected async override Task<ResponseInfo> GetResponseInfo()
{
await EnsureSourceStream().ConfigureAwait(false);
return _sourceStream;
}
string path = await GetImagePath().ConfigureAwait(false);
private bool _sourceStreamEnsured;
private async Task EnsureSourceStream()
{
if (!_sourceStreamEnsured)
ResponseInfo info = new ResponseInfo
{
try
CacheDuration = TimeSpan.FromDays(365),
ContentType = MimeTypes.GetMimeType(path)
};
DateTime? date = File.GetLastWriteTimeUtc(path);
// If the file does not exist it will return jan 1, 1601
// http://msdn.microsoft.com/en-us/library/system.io.file.getlastwritetimeutc.aspx
if (date.Value.Year == 1601)
{
if (!File.Exists(path))
{
_sourceStream = File.OpenRead(await GetImagePath().ConfigureAwait(false));
}
catch (FileNotFoundException ex)
{
StatusCode = 404;
Logger.LogException(ex);
}
catch (DirectoryNotFoundException ex)
{
StatusCode = 404;
Logger.LogException(ex);
}
catch (UnauthorizedAccessException ex)
{
StatusCode = 403;
Logger.LogException(ex);
}
finally
{
_sourceStreamEnsured = true;
info.StatusCode = 404;
date = null;
}
}
info.DateLastModified = date;
return info;
}
public async override Task<string> GetContentType()
{
await EnsureSourceStream().ConfigureAwait(false);
if (await GetSourceStream().ConfigureAwait(false) == null)
{
return null;
}
return MimeTypes.GetMimeType(await GetImagePath().ConfigureAwait(false));
}
public override TimeSpan CacheDuration
private int ImageIndex
{
get
{
return TimeSpan.FromDays(365);
string val = QueryString["index"];
if (string.IsNullOrEmpty(val))
{
return 0;
}
return int.Parse(val);
}
}
protected async override Task<DateTime?> GetLastDateModified()
{
await EnsureSourceStream().ConfigureAwait(false);
if (await GetSourceStream().ConfigureAwait(false) == null)
{
return null;
}
return File.GetLastWriteTimeUtc(await GetImagePath().ConfigureAwait(false));
}
private int? Height
{
get
@ -236,33 +216,9 @@ namespace MediaBrowser.Api.HttpHandlers
protected override async Task WriteResponseToOutputStream(Stream stream)
{
ImageProcessor.ProcessImage(await GetSourceStream().ConfigureAwait(false), stream, Width, Height, MaxWidth, MaxHeight, Quality);
}
var entity = await GetSourceEntity().ConfigureAwait(false);
private string GetImagePathFromTypes(BaseItem item, ImageType imageType, int imageIndex)
{
if (imageType == ImageType.Logo)
{
return item.LogoImagePath;
}
if (imageType == ImageType.Backdrop)
{
return item.BackdropImagePaths.ElementAt(imageIndex);
}
if (imageType == ImageType.Banner)
{
return item.BannerImagePath;
}
if (imageType == ImageType.Art)
{
return item.ArtImagePath;
}
if (imageType == ImageType.Thumbnail)
{
return item.ThumbnailImagePath;
}
return item.PrimaryImagePath;
ImageProcessor.ProcessImage(entity, ImageType, ImageIndex, stream, Width, Height, MaxWidth, MaxHeight, Quality);
}
}
}

View File

@ -15,8 +15,8 @@ namespace MediaBrowser.Api.HttpHandlers
{
return ApiService.IsApiUrlMatch("pluginassembly", request);
}
public override Task<string> GetContentType()
protected override Task<ResponseInfo> GetResponseInfo()
{
throw new NotImplementedException();
}

View File

@ -17,7 +17,7 @@ namespace MediaBrowser.Api.HttpHandlers
{
return ApiService.IsApiUrlMatch("pluginconfiguration", request);
}
private BasePlugin _plugin;
private BasePlugin Plugin
{
@ -39,18 +39,15 @@ namespace MediaBrowser.Api.HttpHandlers
return Task.FromResult(Plugin.Configuration);
}
public override TimeSpan CacheDuration
protected override async Task<ResponseInfo> GetResponseInfo()
{
get
{
return TimeSpan.FromDays(7);
}
}
var info = await base.GetResponseInfo().ConfigureAwait(false);
protected override Task<DateTime?> GetLastDateModified()
{
return Task.FromResult<DateTime?>(Plugin.ConfigurationDateLastModified);
}
info.DateLastModified = Plugin.ConfigurationDateLastModified;
info.CacheDuration = TimeSpan.FromDays(7);
return info;
}
}
}

View File

@ -16,23 +16,22 @@ namespace MediaBrowser.Api.HttpHandlers
{
return ApiService.IsApiUrlMatch("serverconfiguration", request);
}
protected override Task<ServerConfiguration> GetObjectToSerialize()
{
return Task.FromResult(Kernel.Instance.Configuration);
}
public override TimeSpan CacheDuration
protected override async Task<ResponseInfo> GetResponseInfo()
{
get
{
return TimeSpan.FromDays(7);
}
}
var info = await base.GetResponseInfo().ConfigureAwait(false);
protected override Task<DateTime?> GetLastDateModified()
{
return Task.FromResult<DateTime?>(File.GetLastWriteTimeUtc(Kernel.Instance.ApplicationPaths.SystemConfigurationFilePath));
info.DateLastModified =
File.GetLastWriteTimeUtc(Kernel.Instance.ApplicationPaths.SystemConfigurationFilePath);
info.CacheDuration = TimeSpan.FromDays(7);
return info;
}
}
}

View File

@ -1,5 +1,5 @@
using MediaBrowser.Common.Drawing;
using MediaBrowser.Common.Net.Handlers;
using MediaBrowser.Common.Net.Handlers;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.DTO;
using MediaBrowser.Model.Entities;
@ -7,7 +7,6 @@ using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;

View File

@ -3,6 +3,7 @@ using MediaBrowser.Controller;
using MediaBrowser.Model.Weather;
using System;
using System.ComponentModel.Composition;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
@ -15,7 +16,7 @@ namespace MediaBrowser.Api.HttpHandlers
{
return ApiService.IsApiUrlMatch("weather", request);
}
protected override Task<WeatherInfo> GetObjectToSerialize()
{
// If a specific zip code was requested on the query string, use that. Otherwise use the value from configuration
@ -27,18 +28,16 @@ namespace MediaBrowser.Api.HttpHandlers
zipCode = Kernel.Instance.Configuration.WeatherZipCode;
}
return Kernel.Instance.WeatherClient.GetWeatherInfoAsync(zipCode);
return Kernel.Instance.WeatherProviders.First().GetWeatherInfoAsync(zipCode);
}
/// <summary>
/// Tell the client to cache the weather info for 15 minutes
/// </summary>
public override TimeSpan CacheDuration
protected override async Task<ResponseInfo> GetResponseInfo()
{
get
{
return TimeSpan.FromMinutes(15);
}
var info = await base.GetResponseInfo().ConfigureAwait(false);
info.CacheDuration = TimeSpan.FromMinutes(15);
return info;
}
}
}

View File

@ -83,7 +83,6 @@
<Compile Include="HttpHandlers\WeatherHandler.cs" />
<Compile Include="HttpHandlers\YearHandler.cs" />
<Compile Include="HttpHandlers\YearsHandler.cs" />
<Compile Include="ImageProcessor.cs" />
<Compile Include="Plugin.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>

View File

@ -1,5 +1,4 @@
using MediaBrowser.Common.Plugins;
using MediaBrowser.Model.Plugins;
using System.ComponentModel.Composition;
namespace MediaBrowser.Api

View File

@ -0,0 +1,12 @@
using System;
namespace MediaBrowser.Common.Events
{
/// <summary>
/// Provides a generic EventArgs subclass that can hold any kind of object
/// </summary>
public class GenericEventArgs<T> : EventArgs
{
public T Argument { get; set; }
}
}

View File

@ -1,4 +1,5 @@
using MediaBrowser.Common.Logging;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Logging;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Net.Handlers;
using MediaBrowser.Common.Plugins;
@ -9,7 +10,6 @@ using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
@ -24,6 +24,34 @@ namespace MediaBrowser.Common.Kernel
where TConfigurationType : BaseApplicationConfiguration, new()
where TApplicationPathsType : BaseApplicationPaths, new()
{
#region ReloadBeginning Event
/// <summary>
/// Fires whenever the kernel begins reloading
/// </summary>
public event EventHandler<GenericEventArgs<IProgress<TaskProgress>>> ReloadBeginning;
private void OnReloadBeginning(IProgress<TaskProgress> progress)
{
if (ReloadBeginning != null)
{
ReloadBeginning(this, new GenericEventArgs<IProgress<TaskProgress>> { Argument = progress });
}
}
#endregion
#region ReloadCompleted Event
/// <summary>
/// Fires whenever the kernel completes reloading
/// </summary>
public event EventHandler<GenericEventArgs<IProgress<TaskProgress>>> ReloadCompleted;
private void OnReloadCompleted(IProgress<TaskProgress> progress)
{
if (ReloadCompleted != null)
{
ReloadCompleted(this, new GenericEventArgs<IProgress<TaskProgress>> { Argument = progress });
}
}
#endregion
/// <summary>
/// Gets the current configuration
/// </summary>
@ -43,6 +71,12 @@ namespace MediaBrowser.Common.Kernel
[ImportMany(typeof(BaseHandler))]
private IEnumerable<BaseHandler> HttpHandlers { get; set; }
/// <summary>
/// Gets the list of currently registered Loggers
/// </summary>
[ImportMany(typeof(BaseLogger))]
public IEnumerable<BaseLogger> Loggers { get; set; }
/// <summary>
/// Both the Ui and server will have a built-in HttpServer.
/// People will inevitably want remote control apps so it's needed in the Ui too.
@ -54,6 +88,8 @@ namespace MediaBrowser.Common.Kernel
/// </summary>
private IDisposable HttpListener { get; set; }
private CompositionContainer CompositionContainer { get; set; }
protected virtual string HttpServerUrlPrefix
{
get
@ -72,13 +108,13 @@ namespace MediaBrowser.Common.Kernel
/// </summary>
public async Task Init(IProgress<TaskProgress> progress)
{
Logger.Kernel = this;
// Performs initializations that only occur once
InitializeInternal(progress);
// Performs initializations that can be reloaded at anytime
await Reload(progress).ConfigureAwait(false);
progress.Report(new TaskProgress { Description = "Loading Complete" });
}
/// <summary>
@ -87,45 +123,40 @@ namespace MediaBrowser.Common.Kernel
protected virtual void InitializeInternal(IProgress<TaskProgress> progress)
{
ApplicationPaths = new TApplicationPathsType();
ReloadLogger();
progress.Report(new TaskProgress { Description = "Loading configuration" });
ReportProgress(progress, "Loading Configuration");
ReloadConfiguration();
progress.Report(new TaskProgress { Description = "Starting Http server" });
ReportProgress(progress, "Loading Http Server");
ReloadHttpServer();
}
/// <summary>
/// Performs initializations that can be reloaded at anytime
/// </summary>
public virtual async Task Reload(IProgress<TaskProgress> progress)
public async Task Reload(IProgress<TaskProgress> progress)
{
OnReloadBeginning(progress);
await ReloadInternal(progress).ConfigureAwait(false);
OnReloadCompleted(progress);
ReportProgress(progress, "Kernel.Reload Complete");
}
/// <summary>
/// Performs initializations that can be reloaded at anytime
/// </summary>
protected virtual async Task ReloadInternal(IProgress<TaskProgress> progress)
{
await Task.Run(() =>
{
progress.Report(new TaskProgress { Description = "Loading Plugins" });
ReportProgress(progress, "Loading Plugins");
ReloadComposableParts();
}).ConfigureAwait(false);
}
/// <summary>
/// Disposes the current logger and creates a new one
/// </summary>
private void ReloadLogger()
{
DisposeLogger();
DateTime now = DateTime.Now;
string logFilePath = Path.Combine(ApplicationPaths.LogDirectoryPath, "log-" + now.ToString("dMyyyy") + "-" + now.Ticks + ".log");
Trace.Listeners.Add(new TextWriterTraceListener(logFilePath));
Trace.AutoFlush = true;
Logger.LoggerInstance = new TraceLogger();
}
/// <summary>
/// Uses MEF to locate plugins
@ -135,14 +166,13 @@ namespace MediaBrowser.Common.Kernel
{
DisposeComposableParts();
var container = GetCompositionContainer(includeCurrentAssembly: true);
CompositionContainer = GetCompositionContainer(includeCurrentAssembly: true);
container.ComposeParts(this);
CompositionContainer.ComposeParts(this);
OnComposablePartsLoaded();
container.Catalog.Dispose();
container.Dispose();
CompositionContainer.Catalog.Dispose();
}
/// <summary>
@ -157,8 +187,7 @@ namespace MediaBrowser.Common.Kernel
var catalog = new AggregateCatalog(pluginAssemblies.Select(a => new AssemblyCatalog(a)));
// Include composable parts in the Common assembly
// Uncomment this if it's ever needed
//catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
if (includeCurrentAssembly)
{
@ -174,8 +203,13 @@ namespace MediaBrowser.Common.Kernel
/// </summary>
protected virtual void OnComposablePartsLoaded()
{
foreach (var logger in Loggers)
{
logger.Initialize(this);
}
// Start-up each plugin
foreach (BasePlugin plugin in Plugins)
foreach (var plugin in Plugins)
{
plugin.Initialize(this);
}
@ -189,17 +223,16 @@ namespace MediaBrowser.Common.Kernel
//Configuration information for anything other than server-specific configuration will have to come via the API... -ebr
// Deserialize config
if (!File.Exists(ApplicationPaths.SystemConfigurationFilePath))
// Use try/catch to avoid the extra file system lookup using File.Exists
try
{
Configuration = XmlSerializer.DeserializeFromFile<TConfigurationType>(ApplicationPaths.SystemConfigurationFilePath);
}
catch (FileNotFoundException)
{
Configuration = new TConfigurationType();
XmlSerializer.SerializeToFile(Configuration, ApplicationPaths.SystemConfigurationFilePath);
}
else
{
Configuration = XmlSerializer.DeserializeFromFile<TConfigurationType>(ApplicationPaths.SystemConfigurationFilePath);
}
Logger.LoggerInstance.LogSeverity = Configuration.EnableDebugLevelLogging ? LogSeverity.Debug : LogSeverity.Info;
}
/// <summary>
@ -233,12 +266,10 @@ namespace MediaBrowser.Common.Kernel
public virtual void Dispose()
{
Logger.LogInfo("Beginning Kernel.Dispose");
DisposeComposableParts();
DisposeHttpServer();
DisposeLogger();
DisposeComposableParts();
}
/// <summary>
@ -246,22 +277,9 @@ namespace MediaBrowser.Common.Kernel
/// </summary>
protected virtual void DisposeComposableParts()
{
DisposePlugins();
}
/// <summary>
/// Disposes all plugins
/// </summary>
private void DisposePlugins()
{
if (Plugins != null)
if (CompositionContainer != null)
{
Logger.LogInfo("Disposing Plugins");
foreach (BasePlugin plugin in Plugins)
{
plugin.Dispose();
}
CompositionContainer.Dispose();
}
}
@ -273,7 +291,7 @@ namespace MediaBrowser.Common.Kernel
if (HttpServer != null)
{
Logger.LogInfo("Disposing Http Server");
HttpServer.Dispose();
}
@ -283,21 +301,6 @@ namespace MediaBrowser.Common.Kernel
}
}
/// <summary>
/// Disposes the current Logger instance
/// </summary>
private void DisposeLogger()
{
Trace.Listeners.Clear();
if (Logger.LoggerInstance != null)
{
Logger.LogInfo("Disposing Logger");
Logger.LoggerInstance.Dispose();
}
}
/// <summary>
/// Gets the current application version
/// </summary>
@ -309,6 +312,13 @@ namespace MediaBrowser.Common.Kernel
}
}
protected void ReportProgress(IProgress<TaskProgress> progress, string message)
{
progress.Report(new TaskProgress { Description = message });
Logger.LogInfo(message);
}
BaseApplicationPaths IKernel.ApplicationPaths
{
get { return ApplicationPaths; }
@ -322,6 +332,7 @@ namespace MediaBrowser.Common.Kernel
Task Init(IProgress<TaskProgress> progress);
Task Reload(IProgress<TaskProgress> progress);
IEnumerable<BaseLogger> Loggers { get; }
void Dispose();
}
}

View File

@ -1,92 +1,16 @@
using System;
using System.Text;
using System.Threading;
using MediaBrowser.Common.Kernel;
using System;
namespace MediaBrowser.Common.Logging
{
public abstract class BaseLogger : IDisposable
{
public LogSeverity LogSeverity { get; set; }
public void LogInfo(string message, params object[] paramList)
{
LogEntry(message, LogSeverity.Info, paramList);
}
public void LogDebugInfo(string message, params object[] paramList)
{
LogEntry(message, LogSeverity.Debug, paramList);
}
public void LogError(string message, params object[] paramList)
{
LogEntry(message, LogSeverity.Error, paramList);
}
public void LogException(string message, Exception exception, params object[] paramList)
{
var builder = new StringBuilder();
if (exception != null)
{
builder.AppendFormat("Exception. Type={0} Msg={1} StackTrace={3}{2}",
exception.GetType().FullName,
exception.Message,
exception.StackTrace,
Environment.NewLine);
}
message = FormatMessage(message, paramList);
LogError(string.Format("{0} ( {1} )", message, builder));
}
public void LogWarning(string message, params object[] paramList)
{
LogEntry(message, LogSeverity.Warning, paramList);
}
private string FormatMessage(string message, params object[] paramList)
{
if (paramList != null)
{
for (int i = 0; i < paramList.Length; i++)
{
message = message.Replace("{" + i + "}", paramList[i].ToString());
}
}
return message;
}
private void LogEntry(string message, LogSeverity severity, params object[] paramList)
{
if (severity < LogSeverity) return;
message = FormatMessage(message, paramList);
Thread currentThread = Thread.CurrentThread;
var row = new LogRow
{
Severity = severity,
Message = message,
ThreadId = currentThread.ManagedThreadId,
ThreadName = currentThread.Name,
Time = DateTime.Now
};
LogEntry(row);
}
protected virtual void Flush()
{
}
public abstract void Initialize(IKernel kernel);
public abstract void LogEntry(LogRow row);
public virtual void Dispose()
{
Logger.LogInfo("Disposing " + GetType().Name);
}
protected abstract void LogEntry(LogRow row);
}
}

View File

@ -1,4 +1,4 @@
using System;
using System;
namespace MediaBrowser.Common.Logging
{
@ -11,4 +11,4 @@ namespace MediaBrowser.Common.Logging
Warning = 4,
Error = 8
}
}
}

View File

@ -1,24 +1,28 @@
using System;
using System.Diagnostics;
using System.Text;
using System.Threading;
using MediaBrowser.Common.Kernel;
namespace MediaBrowser.Common.Logging
{
public static class Logger
{
public static BaseLogger LoggerInstance { get; set; }
internal static IKernel Kernel { get; set; }
public static void LogInfo(string message, params object[] paramList)
{
LoggerInstance.LogInfo(message, paramList);
LogEntry(message, LogSeverity.Info, paramList);
}
public static void LogDebugInfo(string message, params object[] paramList)
{
LoggerInstance.LogDebugInfo(message, paramList);
LogEntry(message, LogSeverity.Debug, paramList);
}
public static void LogError(string message, params object[] paramList)
{
LoggerInstance.LogError(message, paramList);
LogEntry(message, LogSeverity.Error, paramList);
}
public static void LogException(Exception ex, params object[] paramList)
@ -28,12 +32,62 @@ namespace MediaBrowser.Common.Logging
public static void LogException(string message, Exception ex, params object[] paramList)
{
LoggerInstance.LogException(message, ex, paramList);
var builder = new StringBuilder();
if (ex != null)
{
builder.AppendFormat("Exception. Type={0} Msg={1} StackTrace={3}{2}",
ex.GetType().FullName,
ex.Message,
ex.StackTrace,
Environment.NewLine);
}
message = FormatMessage(message, paramList);
LogError(string.Format("{0} ( {1} )", message, builder));
}
public static void LogWarning(string message, params object[] paramList)
{
LoggerInstance.LogWarning(message, paramList);
LogEntry(message, LogSeverity.Warning, paramList);
}
private static void LogEntry(string message, LogSeverity severity, params object[] paramList)
{
message = FormatMessage(message, paramList);
Thread currentThread = Thread.CurrentThread;
var row = new LogRow
{
Severity = severity,
Message = message,
ThreadId = currentThread.ManagedThreadId,
ThreadName = currentThread.Name,
Time = DateTime.Now
};
if (Kernel.Loggers != null)
{
foreach (var logger in Kernel.Loggers)
{
logger.LogEntry(row);
}
}
}
private static string FormatMessage(string message, params object[] paramList)
{
if (paramList != null)
{
for (int i = 0; i < paramList.Length; i++)
{
message = message.Replace("{" + i + "}", paramList[i].ToString());
}
}
return message;
}
}
}

View File

@ -1,37 +0,0 @@
using System;
using System.IO;
using System.Text;
namespace MediaBrowser.Common.Logging
{
/// <summary>
/// Provides a Logger that can write to any Stream
/// </summary>
public class StreamLogger : BaseLogger
{
private Stream Stream { get; set; }
public StreamLogger(Stream stream)
: base()
{
Stream = stream;
}
protected override void LogEntry(LogRow row)
{
byte[] bytes = new UTF8Encoding().GetBytes(row.ToString() + Environment.NewLine);
lock (Stream)
{
Stream.Write(bytes, 0, bytes.Length);
Stream.Flush();
}
}
public override void Dispose()
{
base.Dispose();
Stream.Dispose();
}
}
}

View File

@ -0,0 +1,38 @@
using MediaBrowser.Common.Kernel;
using System;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.IO;
namespace MediaBrowser.Common.Logging
{
[Export(typeof(BaseLogger))]
public class TraceFileLogger : BaseLogger
{
private TraceListener Listener { get; set; }
public override void Initialize(IKernel kernel)
{
DateTime now = DateTime.Now;
string logFilePath = Path.Combine(kernel.ApplicationPaths.LogDirectoryPath, "log-" + now.ToString("dMyyyy") + "-" + now.Ticks + ".log");
Listener = new TextWriterTraceListener(logFilePath);
Trace.Listeners.Add(Listener);
Trace.AutoFlush = true;
}
public override void Dispose()
{
base.Dispose();
Trace.Listeners.Remove(Listener);
Listener.Dispose();
}
public override void LogEntry(LogRow row)
{
Trace.WriteLine(row.ToString());
}
}
}

View File

@ -1,12 +0,0 @@
using System.Diagnostics;
namespace MediaBrowser.Common.Logging
{
public class TraceLogger : BaseLogger
{
protected override void LogEntry(LogRow row)
{
Trace.WriteLine(row.ToString());
}
}
}

View File

@ -82,9 +82,11 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Extensions\BaseExtensions.cs" />
<Compile Include="Events\GenericEventArgs.cs" />
<Compile Include="Kernel\BaseApplicationPaths.cs" />
<Compile Include="Drawing\DrawingUtils.cs" />
<Compile Include="Logging\TraceLogger.cs" />
<Compile Include="Logging\BaseLogger.cs" />
<Compile Include="Logging\LogSeverity.cs" />
<Compile Include="Logging\TraceFileLogger.cs" />
<Compile Include="Net\Handlers\StaticFileHandler.cs" />
<Compile Include="Net\MimeTypes.cs" />
<Compile Include="Plugins\BaseTheme.cs" />
@ -96,11 +98,8 @@
<Compile Include="Serialization\JsonSerializer.cs" />
<Compile Include="Kernel\BaseKernel.cs" />
<Compile Include="Kernel\KernelContext.cs" />
<Compile Include="Logging\BaseLogger.cs" />
<Compile Include="Logging\Logger.cs" />
<Compile Include="Logging\LogRow.cs" />
<Compile Include="Logging\LogSeverity.cs" />
<Compile Include="Logging\StreamLogger.cs" />
<Compile Include="Net\Handlers\BaseEmbeddedResourceHandler.cs" />
<Compile Include="Net\Handlers\BaseHandler.cs" />
<Compile Include="Net\Handlers\BaseSerializationHandler.cs" />
@ -152,6 +151,7 @@
<ItemGroup>
<Resource Include="Resources\Images\spinner.gif" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -13,11 +13,6 @@ namespace MediaBrowser.Common.Net.Handlers
protected string ResourcePath { get; set; }
public override Task<string> GetContentType()
{
return Task.FromResult(MimeTypes.GetMimeType(ResourcePath));
}
protected override Task WriteResponseToOutputStream(Stream stream)
{
return GetEmbeddedResourceStream().CopyToAsync(stream);

View File

@ -112,32 +112,6 @@ namespace MediaBrowser.Common.Net.Handlers
}
}
/// <summary>
/// Gets the MIME type to include in the response headers
/// </summary>
public abstract Task<string> GetContentType();
/// <summary>
/// Gets the status code to include in the response headers
/// </summary>
protected int StatusCode { get; set; }
/// <summary>
/// Gets the cache duration to include in the response headers
/// </summary>
public virtual TimeSpan CacheDuration
{
get
{
return TimeSpan.FromTicks(0);
}
}
public virtual bool ShouldCompressResponse(string contentType)
{
return true;
}
private bool ClientSupportsCompression
{
get
@ -186,15 +160,21 @@ namespace MediaBrowser.Common.Net.Handlers
ctx.Response.Headers["Accept-Ranges"] = "bytes";
}
// Set the initial status code
// When serving a range request, we need to return status code 206 to indicate a partial response body
StatusCode = SupportsByteRangeRequests && IsRangeRequest ? 206 : 200;
ResponseInfo responseInfo = await GetResponseInfo().ConfigureAwait(false);
ctx.Response.ContentType = await GetContentType().ConfigureAwait(false);
if (responseInfo.IsResponseValid)
{
// Set the initial status code
// When serving a range request, we need to return status code 206 to indicate a partial response body
responseInfo.StatusCode = SupportsByteRangeRequests && IsRangeRequest ? 206 : 200;
}
TimeSpan cacheDuration = CacheDuration;
ctx.Response.ContentType = responseInfo.ContentType;
DateTime? lastDateModified = await GetLastDateModified().ConfigureAwait(false);
if (!string.IsNullOrEmpty(responseInfo.Etag))
{
ctx.Response.Headers["ETag"] = responseInfo.Etag;
}
if (ctx.Request.Headers.AllKeys.Contains("If-Modified-Since"))
{
@ -203,26 +183,26 @@ namespace MediaBrowser.Common.Net.Handlers
if (DateTime.TryParse(ctx.Request.Headers["If-Modified-Since"], out ifModifiedSince))
{
// If the cache hasn't expired yet just return a 304
if (IsCacheValid(ifModifiedSince.ToUniversalTime(), cacheDuration, lastDateModified))
if (IsCacheValid(ifModifiedSince.ToUniversalTime(), responseInfo.CacheDuration, responseInfo.DateLastModified))
{
StatusCode = 304;
// ETag must also match (if supplied)
if ((responseInfo.Etag ?? string.Empty).Equals(ctx.Request.Headers["If-None-Match"] ?? string.Empty))
{
responseInfo.StatusCode = 304;
}
}
}
}
await PrepareResponse().ConfigureAwait(false);
Logger.LogInfo("Responding with status code {0} for url {1}", responseInfo.StatusCode, url);
Logger.LogInfo("Responding with status code {0} for url {1}", StatusCode, url);
if (IsResponseValid)
if (responseInfo.IsResponseValid)
{
bool compressResponse = ShouldCompressResponse(ctx.Response.ContentType) && ClientSupportsCompression;
await ProcessUncachedRequest(ctx, compressResponse, cacheDuration, lastDateModified).ConfigureAwait(false);
await ProcessUncachedRequest(ctx, responseInfo).ConfigureAwait(false);
}
else
{
ctx.Response.StatusCode = StatusCode;
ctx.Response.StatusCode = responseInfo.StatusCode;
ctx.Response.SendChunked = false;
}
}
@ -239,7 +219,7 @@ namespace MediaBrowser.Common.Net.Handlers
}
}
private async Task ProcessUncachedRequest(HttpListenerContext ctx, bool compressResponse, TimeSpan cacheDuration, DateTime? lastDateModified)
private async Task ProcessUncachedRequest(HttpListenerContext ctx, ResponseInfo responseInfo)
{
long? totalContentLength = TotalContentLength;
@ -258,22 +238,29 @@ namespace MediaBrowser.Common.Net.Handlers
ctx.Response.ContentLength64 = totalContentLength.Value;
}
var compressResponse = responseInfo.CompressResponse && ClientSupportsCompression;
// Add the compression header
if (compressResponse)
{
ctx.Response.AddHeader("Content-Encoding", CompressionMethod);
}
// Add caching headers
if (cacheDuration.Ticks > 0)
if (responseInfo.DateLastModified.HasValue)
{
CacheResponse(ctx.Response, cacheDuration, lastDateModified);
ctx.Response.Headers[HttpResponseHeader.LastModified] = responseInfo.DateLastModified.Value.ToString("r");
}
// Add caching headers
if (responseInfo.CacheDuration.Ticks > 0)
{
CacheResponse(ctx.Response, responseInfo.CacheDuration);
}
// Set the status code
ctx.Response.StatusCode = StatusCode;
ctx.Response.StatusCode = responseInfo.StatusCode;
if (IsResponseValid)
if (responseInfo.IsResponseValid)
{
// Finally, write the response data
Stream outputStream = ctx.Response.OutputStream;
@ -300,23 +287,10 @@ namespace MediaBrowser.Common.Net.Handlers
}
}
private void CacheResponse(HttpListenerResponse response, TimeSpan duration, DateTime? dateModified)
private void CacheResponse(HttpListenerResponse response, TimeSpan duration)
{
DateTime now = DateTime.UtcNow;
DateTime lastModified = dateModified ?? now;
response.Headers[HttpResponseHeader.CacheControl] = "public, max-age=" + Convert.ToInt32(duration.TotalSeconds);
response.Headers[HttpResponseHeader.Expires] = now.Add(duration).ToString("r");
response.Headers[HttpResponseHeader.LastModified] = lastModified.ToString("r");
}
/// <summary>
/// Gives subclasses a chance to do any prep work, and also to validate data and set an error status code, if needed
/// </summary>
protected virtual Task PrepareResponse()
{
return Task.FromResult<object>(null);
response.Headers[HttpResponseHeader.Expires] = DateTime.UtcNow.Add(duration).ToString("r");
}
protected abstract Task WriteResponseToOutputStream(Stream stream);
@ -364,20 +338,7 @@ namespace MediaBrowser.Common.Net.Handlers
return null;
}
protected virtual Task<DateTime?> GetLastDateModified()
{
DateTime? value = null;
return Task.FromResult(value);
}
private bool IsResponseValid
{
get
{
return StatusCode == 200 || StatusCode == 206;
}
}
protected abstract Task<ResponseInfo> GetResponseInfo();
private Hashtable _formValues;
@ -439,4 +400,31 @@ namespace MediaBrowser.Common.Net.Handlers
return formVars;
}
}
public class ResponseInfo
{
public string ContentType { get; set; }
public string Etag { get; set; }
public DateTime? DateLastModified { get; set; }
public TimeSpan CacheDuration { get; set; }
public bool CompressResponse { get; set; }
public int StatusCode { get; set; }
public ResponseInfo()
{
CacheDuration = TimeSpan.FromTicks(0);
CompressResponse = true;
StatusCode = 200;
}
public bool IsResponseValid
{
get
{
return StatusCode == 200 || StatusCode == 206;
}
}
}
}

View File

@ -1,12 +1,12 @@
using System;
using MediaBrowser.Common.Serialization;
using System;
using System.IO;
using System.Threading.Tasks;
using MediaBrowser.Common.Serialization;
namespace MediaBrowser.Common.Net.Handlers
{
public abstract class BaseSerializationHandler<T> : BaseHandler
where T : class
where T : class
{
public SerializationFormat SerializationFormat
{
@ -22,61 +22,61 @@ namespace MediaBrowser.Common.Net.Handlers
return (SerializationFormat)Enum.Parse(typeof(SerializationFormat), format, true);
}
}
public override Task<string> GetContentType()
{
switch (SerializationFormat)
{
case SerializationFormat.Jsv:
return Task.FromResult("text/plain");
case SerializationFormat.Protobuf:
return Task.FromResult("application/x-protobuf");
default:
return Task.FromResult(MimeTypes.JsonMimeType);
}
}
private bool _objectToSerializeEnsured;
private T _objectToSerialize;
private async Task EnsureObjectToSerialize()
protected string ContentType
{
if (!_objectToSerializeEnsured)
get
{
_objectToSerialize = await GetObjectToSerialize().ConfigureAwait(false);
if (_objectToSerialize == null)
switch (SerializationFormat)
{
StatusCode = 404;
case SerializationFormat.Jsv:
return "text/plain";
case SerializationFormat.Protobuf:
return "application/x-protobuf";
default:
return MimeTypes.JsonMimeType;
}
_objectToSerializeEnsured = true;
}
}
protected override async Task<ResponseInfo> GetResponseInfo()
{
ResponseInfo info = new ResponseInfo
{
ContentType = ContentType
};
_objectToSerialize = await GetObjectToSerialize().ConfigureAwait(false);
if (_objectToSerialize == null)
{
info.StatusCode = 404;
}
return info;
}
private T _objectToSerialize;
protected abstract Task<T> GetObjectToSerialize();
protected override Task PrepareResponse()
protected override Task WriteResponseToOutputStream(Stream stream)
{
return EnsureObjectToSerialize();
}
protected async override Task WriteResponseToOutputStream(Stream stream)
{
await EnsureObjectToSerialize().ConfigureAwait(false);
switch (SerializationFormat)
return Task.Run(() =>
{
case SerializationFormat.Jsv:
JsvSerializer.SerializeToStream(_objectToSerialize, stream);
break;
case SerializationFormat.Protobuf:
ProtobufSerializer.SerializeToStream(_objectToSerialize, stream);
break;
default:
JsonSerializer.SerializeToStream(_objectToSerialize, stream);
break;
}
switch (SerializationFormat)
{
case SerializationFormat.Jsv:
JsvSerializer.SerializeToStream(_objectToSerialize, stream);
break;
case SerializationFormat.Protobuf:
ProtobufSerializer.SerializeToStream(_objectToSerialize, stream);
break;
default:
JsonSerializer.SerializeToStream(_objectToSerialize, stream);
break;
}
});
}
}

View File

@ -33,46 +33,7 @@ namespace MediaBrowser.Common.Net.Handlers
}
}
private bool _sourceStreamEnsured;
private Stream _sourceStream;
private Stream SourceStream
{
get
{
EnsureSourceStream();
return _sourceStream;
}
}
private void EnsureSourceStream()
{
if (!_sourceStreamEnsured)
{
try
{
_sourceStream = File.OpenRead(Path);
}
catch (FileNotFoundException ex)
{
StatusCode = 404;
Logger.LogException(ex);
}
catch (DirectoryNotFoundException ex)
{
StatusCode = 404;
Logger.LogException(ex);
}
catch (UnauthorizedAccessException ex)
{
StatusCode = 403;
Logger.LogException(ex);
}
finally
{
_sourceStreamEnsured = true;
}
}
}
private Stream SourceStream { get; set; }
protected override bool SupportsByteRangeRequests
{
@ -82,7 +43,7 @@ namespace MediaBrowser.Common.Net.Handlers
}
}
public override bool ShouldCompressResponse(string contentType)
private bool ShouldCompressResponse(string contentType)
{
// Can't compress these
if (IsRangeRequest)
@ -105,29 +66,41 @@ namespace MediaBrowser.Common.Net.Handlers
return SourceStream.Length;
}
protected override Task<DateTime?> GetLastDateModified()
protected override Task<ResponseInfo> GetResponseInfo()
{
DateTime? value = null;
ResponseInfo info = new ResponseInfo
{
ContentType = MimeTypes.GetMimeType(Path),
};
EnsureSourceStream();
try
{
SourceStream = File.OpenRead(Path);
}
catch (FileNotFoundException ex)
{
info.StatusCode = 404;
Logger.LogException(ex);
}
catch (DirectoryNotFoundException ex)
{
info.StatusCode = 404;
Logger.LogException(ex);
}
catch (UnauthorizedAccessException ex)
{
info.StatusCode = 403;
Logger.LogException(ex);
}
info.CompressResponse = ShouldCompressResponse(info.ContentType);
if (SourceStream != null)
{
value = File.GetLastWriteTimeUtc(Path);
info.DateLastModified = File.GetLastWriteTimeUtc(Path);
}
return Task.FromResult(value);
}
public override Task<string> GetContentType()
{
return Task.FromResult(MimeTypes.GetMimeType(Path));
}
protected override Task PrepareResponse()
{
EnsureSourceStream();
return Task.FromResult<object>(null);
return Task.FromResult<ResponseInfo>(info);
}
protected override Task WriteResponseToOutputStream(Stream stream)

View File

@ -1,4 +1,5 @@
using MediaBrowser.Common.Kernel;
using MediaBrowser.Common.Logging;
using MediaBrowser.Common.Serialization;
using MediaBrowser.Model.Plugins;
using System;
@ -200,6 +201,8 @@ namespace MediaBrowser.Common.Plugins
/// </summary>
public void Dispose()
{
Logger.LogInfo("Disposing {0} Plugin", Name);
if (Context == KernelContext.Server)
{
DisposeOnServer();

View File

@ -5,7 +5,6 @@ using Microsoft.Shell;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows;
namespace MediaBrowser.Common.UI
@ -44,8 +43,6 @@ namespace MediaBrowser.Common.UI
var progress = new Progress<TaskProgress>();
progress.ProgressChanged += progress_ProgressChanged;
var splash = new Splash(progress);
splash.Show();
@ -56,8 +53,6 @@ namespace MediaBrowser.Common.UI
await Kernel.Init(progress);
progress.ProgressChanged -= progress_ProgressChanged;
Logger.LogInfo("Kernel.Init completed in {0} seconds.", (DateTime.UtcNow - now).TotalSeconds);
splash.Close();
@ -69,12 +64,7 @@ namespace MediaBrowser.Common.UI
}
catch (Exception ex)
{
progress.ProgressChanged -= progress_ProgressChanged;
if (Logger.LoggerInstance != null)
{
Logger.LogException(ex);
}
Logger.LogException(ex);
MessageBox.Show("There was an error launching Media Browser: " + ex.Message);
splash.Close();
@ -84,44 +74,8 @@ namespace MediaBrowser.Common.UI
}
}
public async Task ReloadKernel()
{
var progress = new Progress<TaskProgress>();
progress.ProgressChanged += progress_ProgressChanged;
try
{
DateTime now = DateTime.UtcNow;
await Kernel.Reload(progress);
progress.ProgressChanged -= progress_ProgressChanged;
Logger.LogInfo("Kernel.Reload completed in {0} seconds.", (DateTime.UtcNow - now).TotalSeconds);
}
catch (Exception ex)
{
progress.ProgressChanged -= progress_ProgressChanged;
Logger.LogException(ex);
// Shutdown the app with an error code
Shutdown(1);
}
}
void progress_ProgressChanged(object sender, TaskProgress e)
{
if (Logger.LoggerInstance != null)
{
Logger.LogInfo(e.Description);
}
}
protected virtual void OnKernelLoaded()
{
}
protected override void OnExit(ExitEventArgs e)

View File

@ -1,7 +1,7 @@
using System;
using System.Drawing;
namespace MediaBrowser.Common.Drawing
namespace MediaBrowser.Controller.Drawing
{
public static class DrawingUtils
{

View File

@ -1,26 +1,33 @@
using MediaBrowser.Common.Drawing;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Entities;
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
namespace MediaBrowser.Api
namespace MediaBrowser.Controller.Drawing
{
public static class ImageProcessor
{
/// <summary>
/// Resizes an image from a source stream and saves the result to an output stream
/// Processes an image by resizing to target dimensions
/// </summary>
/// <param name="entity">The entity that owns the image</param>
/// <param name="imageType">The image type</param>
/// <param name="imageIndex">The image index (currently only used with backdrops)</param>
/// <param name="toStream">The stream to save the new image to</param>
/// <param name="width">Use if a fixed width is required. Aspect ratio will be preserved.</param>
/// <param name="height">Use if a fixed height is required. Aspect ratio will be preserved.</param>
/// <param name="maxWidth">Use if a max width is required. Aspect ratio will be preserved.</param>
/// <param name="maxHeight">Use if a max height is required. Aspect ratio will be preserved.</param>
/// <param name="quality">Quality level, from 0-100. Currently only applies to JPG. The default value should suffice.</param>
public static void ProcessImage(Stream sourceImageStream, Stream toStream, int? width, int? height, int? maxWidth, int? maxHeight, int? quality)
public static void ProcessImage(BaseEntity entity, ImageType imageType, int imageIndex, Stream toStream, int? width, int? height, int? maxWidth, int? maxHeight, int? quality)
{
Image originalImage = Image.FromStream(sourceImageStream);
Image originalImage = Image.FromFile(GetImagePath(entity, imageType, imageIndex));
// Determine the output size based on incoming parameters
Size newSize = DrawingUtils.Resize(originalImage.Size, width, height, maxWidth, maxHeight);
Bitmap thumbnail;
@ -35,6 +42,9 @@ namespace MediaBrowser.Api
thumbnail = new Bitmap(newSize.Width, newSize.Height, originalImage.PixelFormat);
}
thumbnail.MakeTransparent();
// Preserve the original resolution
thumbnail.SetResolution(originalImage.HorizontalResolution, originalImage.VerticalResolution);
Graphics thumbnailGraph = Graphics.FromImage(thumbnail);
@ -47,32 +57,66 @@ namespace MediaBrowser.Api
thumbnailGraph.DrawImage(originalImage, 0, 0, newSize.Width, newSize.Height);
Write(originalImage, thumbnail, toStream, quality);
ImageFormat outputFormat = originalImage.RawFormat;
// Write to the output stream
SaveImage(outputFormat, thumbnail, toStream, quality);
thumbnailGraph.Dispose();
thumbnail.Dispose();
originalImage.Dispose();
}
private static void Write(Image originalImage, Image newImage, Stream toStream, int? quality)
public static string GetImagePath(BaseEntity entity, ImageType imageType, int imageIndex)
{
var item = entity as BaseItem;
if (item != null)
{
if (imageType == ImageType.Logo)
{
return item.LogoImagePath;
}
if (imageType == ImageType.Backdrop)
{
return item.BackdropImagePaths.ElementAt(imageIndex);
}
if (imageType == ImageType.Banner)
{
return item.BannerImagePath;
}
if (imageType == ImageType.Art)
{
return item.ArtImagePath;
}
if (imageType == ImageType.Thumbnail)
{
return item.ThumbnailImagePath;
}
}
return entity.PrimaryImagePath;
}
public static void SaveImage(ImageFormat outputFormat, Image newImage, Stream toStream, int? quality)
{
// Use special save methods for jpeg and png that will result in a much higher quality image
// All other formats use the generic Image.Save
if (ImageFormat.Jpeg.Equals(originalImage.RawFormat))
if (ImageFormat.Jpeg.Equals(outputFormat))
{
SaveJpeg(newImage, toStream, quality);
}
else if (ImageFormat.Png.Equals(originalImage.RawFormat))
else if (ImageFormat.Png.Equals(outputFormat))
{
newImage.Save(toStream, ImageFormat.Png);
}
else
{
newImage.Save(toStream, originalImage.RawFormat);
newImage.Save(toStream, outputFormat);
}
}
private static void SaveJpeg(Image newImage, Stream target, int? quality)
public static void SaveJpeg(Image image, Stream target, int? quality)
{
if (!quality.HasValue)
{
@ -82,11 +126,11 @@ namespace MediaBrowser.Api
using (var encoderParameters = new EncoderParameters(1))
{
encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, quality.Value);
newImage.Save(target, GetImageCodeInfo("image/jpeg"), encoderParameters);
image.Save(target, GetImageCodecInfo("image/jpeg"), encoderParameters);
}
}
private static ImageCodecInfo GetImageCodeInfo(string mimeType)
public static ImageCodecInfo GetImageCodecInfo(string mimeType)
{
ImageCodecInfo[] info = ImageCodecInfo.GetImageEncoders();

View File

@ -47,7 +47,7 @@ namespace MediaBrowser.Controller.Entities
public string DisplayMediaType { get; set; }
public float? UserRating { get; set; }
public float? CommunityRating { get; set; }
public long? RunTimeTicks { get; set; }
public string AspectRatio { get; set; }

View File

@ -1,5 +1,6 @@
using MediaBrowser.Common.Kernel;
using MediaBrowser.Common.Logging;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.IO;
@ -43,7 +44,6 @@ namespace MediaBrowser.Controller
public static Kernel Instance { get; private set; }
public ItemController ItemController { get; private set; }
public WeatherClient WeatherClient { get; private set; }
public IEnumerable<User> Users { get; private set; }
public Folder RootFolder { get; private set; }
@ -63,6 +63,12 @@ namespace MediaBrowser.Controller
get { return KernelContext.Server; }
}
/// <summary>
/// Gets the list of currently registered weather prvoiders
/// </summary>
[ImportMany(typeof(BaseWeatherProvider))]
public IEnumerable<BaseWeatherProvider> WeatherProviders { get; private set; }
/// <summary>
/// Gets the list of currently registered metadata prvoiders
/// </summary>
@ -101,28 +107,27 @@ namespace MediaBrowser.Controller
/// </summary>
protected override void InitializeInternal(IProgress<TaskProgress> progress)
{
base.InitializeInternal(progress);
ItemController = new ItemController();
DirectoryWatchers = new DirectoryWatchers();
base.InitializeInternal(progress);
ExtractFFMpeg();
}
/// <summary>
/// Performs initializations that can be reloaded at anytime
/// </summary>
public override async Task Reload(IProgress<TaskProgress> progress)
protected override async Task ReloadInternal(IProgress<TaskProgress> progress)
{
await base.Reload(progress).ConfigureAwait(false);
await base.ReloadInternal(progress).ConfigureAwait(false);
ReloadWeatherClient();
ExtractFFMpeg();
progress.Report(new TaskProgress { Description = "Loading Users" });
ReportProgress(progress, "Loading Users");
ReloadUsers();
progress.Report(new TaskProgress { Description = "Loading Media Library" });
ReportProgress(progress, "Loading Media Library");
await ReloadRoot(allowInternetProviders: false).ConfigureAwait(false);
}
@ -136,8 +141,6 @@ namespace MediaBrowser.Controller
DirectoryWatchers.Stop();
DisposeWeatherClient();
}
protected override void OnComposablePartsLoaded()
@ -380,26 +383,5 @@ namespace MediaBrowser.Controller
}
}
}
/// <summary>
/// Disposes the current WeatherClient
/// </summary>
private void DisposeWeatherClient()
{
if (WeatherClient != null)
{
WeatherClient.Dispose();
}
}
/// <summary>
/// Disposes the current WeatherClient and creates a new one
/// </summary>
private void ReloadWeatherClient()
{
DisposeWeatherClient();
WeatherClient = new WeatherClient();
}
}
}

View File

@ -36,6 +36,7 @@
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Net.Http.WebRequest" />
<Reference Include="System.Reactive.Core, Version=2.0.20823.0, Culture=neutral, PublicKeyToken=f300afd708cefcd3, processorArchitecture=MSIL">
@ -58,6 +59,8 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Drawing\DrawingUtils.cs" />
<Compile Include="Drawing\ImageProcessor.cs" />
<Compile Include="Entities\Audio.cs" />
<Compile Include="Entities\BaseEntity.cs" />
<Compile Include="Entities\BaseItem.cs" />
@ -111,7 +114,8 @@
<Compile Include="Resolvers\BaseItemResolver.cs" />
<Compile Include="Resolvers\FolderResolver.cs" />
<Compile Include="Resolvers\VideoResolver.cs" />
<Compile Include="Weather\WeatherClient.cs" />
<Compile Include="Weather\BaseWeatherProvider.cs" />
<Compile Include="Weather\WeatherProvider.cs" />
<Compile Include="Providers\BaseItemXmlParser.cs" />
<Compile Include="Xml\XmlExtensions.cs" />
</ItemGroup>

View File

@ -220,7 +220,7 @@ namespace MediaBrowser.Controller.Providers
if (float.TryParse(rating, out val))
{
item.UserRating = val;
item.CommunityRating = val;
}
}
break;

View File

@ -0,0 +1,34 @@
using MediaBrowser.Common.Logging;
using MediaBrowser.Model.Weather;
using System;
using System.Net;
using System.Net.Cache;
using System.Net.Http;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Weather
{
public abstract class BaseWeatherProvider : IDisposable
{
protected HttpClient HttpClient { get; private set; }
protected BaseWeatherProvider()
{
var handler = new WebRequestHandler { };
handler.AutomaticDecompression = DecompressionMethods.Deflate;
handler.CachePolicy = new RequestCachePolicy(RequestCacheLevel.Revalidate);
HttpClient = new HttpClient(handler);
}
public virtual void Dispose()
{
Logger.LogInfo("Disposing " + GetType().Name);
HttpClient.Dispose();
}
public abstract Task<WeatherInfo> GetWeatherInfoAsync(string zipCode);
}
}

View File

@ -2,11 +2,9 @@
using MediaBrowser.Common.Serialization;
using MediaBrowser.Model.Weather;
using System;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Cache;
using System.Net.Http;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Weather
@ -15,21 +13,10 @@ namespace MediaBrowser.Controller.Weather
/// Based on http://www.worldweatheronline.com/free-weather-feed.aspx
/// The classes in this file are a reproduction of the json output, which will then be converted to our weather model classes
/// </summary>
public class WeatherClient : IDisposable
[Export(typeof(BaseWeatherProvider))]
public class WeatherProvider : BaseWeatherProvider
{
private HttpClient HttpClient { get; set; }
public WeatherClient()
{
var handler = new WebRequestHandler { };
handler.AutomaticDecompression = DecompressionMethods.Deflate;
handler.CachePolicy = new RequestCachePolicy(RequestCacheLevel.Revalidate);
HttpClient = new HttpClient(handler);
}
public async Task<WeatherInfo> GetWeatherInfoAsync(string zipCode)
public override async Task<WeatherInfo> GetWeatherInfoAsync(string zipCode)
{
if (string.IsNullOrWhiteSpace(zipCode))
{
@ -73,11 +60,6 @@ namespace MediaBrowser.Controller.Weather
return info;
}
public void Dispose()
{
HttpClient.Dispose();
}
}
class WeatherResult

View File

@ -46,7 +46,7 @@ namespace MediaBrowser.Model.DTO
public string DisplayMediaType { get; set; }
[ProtoMember(12)]
public float? UserRating { get; set; }
public float? CommunityRating { get; set; }
[ProtoMember(13)]
public long? RunTimeTicks { get; set; }

View File

@ -1,4 +1,7 @@
using Hardcodet.Wpf.TaskbarNotification;
using MediaBrowser.Common.Events;
using MediaBrowser.Controller;
using MediaBrowser.Model.Progress;
using System;
using System.ComponentModel;
using System.Threading;
@ -11,15 +14,36 @@ namespace MediaBrowser.ServerApplication
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
private Timer LoadingIconTimer { get; set; }
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded;
Loaded += MainWindowLoaded;
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
void MainWindowLoaded(object sender, RoutedEventArgs e)
{
DataContext = this;
Kernel.Instance.ReloadBeginning += KernelReloadBeginning;
Kernel.Instance.ReloadCompleted += KernelReloadCompleted;
}
void KernelReloadBeginning(object sender, GenericEventArgs<IProgress<TaskProgress>> e)
{
MbTaskbarIcon.ShowBalloonTip("Media Browser is reloading", "Please wait...", BalloonIcon.Info);
LoadingImageIndex = 0;
LoadingIconTimer = new Timer(LoadingIconTimerCallback, null, 0, 250);
}
void KernelReloadCompleted(object sender, GenericEventArgs<IProgress<TaskProgress>> e)
{
LoadingIconTimer.Dispose();
LoadingImageIndex = 0;
}
public event PropertyChangedEventHandler PropertyChanged;
@ -62,17 +86,7 @@ namespace MediaBrowser.ServerApplication
private async void cmdReloadServer_click(object sender, RoutedEventArgs e)
{
MbTaskbarIcon.ShowBalloonTip("Media Browser is reloading", "Please wait...", BalloonIcon.Info);
LoadingImageIndex = 0;
Timer timer = new Timer(LoadingIconTimerCallback, null, 0, 250);
await (Application.Current as App).ReloadKernel().ConfigureAwait(false);
timer.Dispose();
LoadingImageIndex = 0;
await Kernel.Instance.Reload(new Progress<TaskProgress>()).ConfigureAwait(false);
}
private void LoadingIconTimerCallback(object stateInfo)

View File

@ -52,4 +52,7 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(Performance) = preSolution
HasPerformanceSessions = true
EndGlobalSection
EndGlobal