Merge with default

pull/702/head
ebr11 Eric Reed spam 13 years ago
commit e2ae376eef

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

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

@ -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,119 +29,99 @@ namespace MediaBrowser.Api.HttpHandlers
return _imagePath;
}
private async Task<string> DiscoverImagePath()
{
string personName = QueryString["personname"];
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);
}
private Stream _sourceStream;
private async Task<Stream> GetSourceStream()
{
await EnsureSourceStream().ConfigureAwait(false);
return _sourceStream;
}
private BaseEntity _sourceEntity;
private bool _sourceStreamEnsured;
private async Task EnsureSourceStream()
private async Task<BaseEntity> GetSourceEntity()
{
if (!_sourceStreamEnsured)
if (_sourceEntity == null)
{
try
if (!string.IsNullOrEmpty(QueryString["personname"]))
{
_sourceStream = File.OpenRead(await GetImagePath().ConfigureAwait(false));
_sourceEntity =
await Kernel.Instance.ItemController.GetPerson(QueryString["personname"]).ConfigureAwait(false);
}
catch (FileNotFoundException ex)
else if (!string.IsNullOrEmpty(QueryString["genre"]))
{
StatusCode = 404;
Logger.LogException(ex);
_sourceEntity =
await Kernel.Instance.ItemController.GetGenre(QueryString["genre"]).ConfigureAwait(false);
}
catch (DirectoryNotFoundException ex)
else if (!string.IsNullOrEmpty(QueryString["year"]))
{
StatusCode = 404;
Logger.LogException(ex);
_sourceEntity =
await
Kernel.Instance.ItemController.GetYear(int.Parse(QueryString["year"])).ConfigureAwait(false);
}
catch (UnauthorizedAccessException ex)
else if (!string.IsNullOrEmpty(QueryString["studio"]))
{
StatusCode = 403;
Logger.LogException(ex);
_sourceEntity =
await Kernel.Instance.ItemController.GetStudio(QueryString["studio"]).ConfigureAwait(false);
}
finally
else if (!string.IsNullOrEmpty(QueryString["userid"]))
{
_sourceStreamEnsured = true;
_sourceEntity = ApiService.GetUserById(QueryString["userid"], false);
}
else
{
_sourceEntity = ApiService.GetItemById(QueryString["id"]);
}
}
return _sourceEntity;
}
public async override Task<string> GetContentType()
private async Task<string> DiscoverImagePath()
{
await EnsureSourceStream().ConfigureAwait(false);
var entity = await GetSourceEntity().ConfigureAwait(false);
return ImageProcessor.GetImagePath(entity, ImageType, ImageIndex);
}
protected async override Task<ResponseInfo> GetResponseInfo()
{
string path = await GetImagePath().ConfigureAwait(false);
ResponseInfo info = new ResponseInfo
{
CacheDuration = TimeSpan.FromDays(365),
ContentType = MimeTypes.GetMimeType(path)
};
if (await GetSourceStream().ConfigureAwait(false) == null)
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)
{
return null;
if (!File.Exists(path))
{
info.StatusCode = 404;
date = null;
}
}
return MimeTypes.GetMimeType(await GetImagePath().ConfigureAwait(false));
info.DateLastModified = date;
return info;
}
public override TimeSpan CacheDuration
private int ImageIndex
{
get
{
return TimeSpan.FromDays(365);
}
}
string val = QueryString["index"];
protected async override Task<DateTime?> GetLastDateModified()
{
await EnsureSourceStream().ConfigureAwait(false);
if (string.IsNullOrEmpty(val))
{
return 0;
}
if (await GetSourceStream().ConfigureAwait(false) == null)
{
return null;
return int.Parse(val);
}
return File.GetLastWriteTimeUtc(await GetImagePath().ConfigureAwait(false));
}
private int? Height
@ -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);
}
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;
}
var entity = await GetSourceEntity().ConfigureAwait(false);
return item.PrimaryImagePath;
ImageProcessor.ProcessImage(entity, ImageType, ImageIndex, stream, Width, Height, MaxWidth, MaxHeight, Quality);
}
}
}

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

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

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

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

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

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

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

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

@ -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,44 +123,39 @@ 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)
{
await Task.Run(() =>
{
progress.Report(new TaskProgress { Description = "Loading Plugins" });
ReloadComposableParts();
OnReloadBeginning(progress);
}).ConfigureAwait(false);
await ReloadInternal(progress).ConfigureAwait(false);
OnReloadCompleted(progress);
ReportProgress(progress, "Kernel.Reload Complete");
}
/// <summary>
/// Disposes the current logger and creates a new one
/// Performs initializations that can be reloaded at anytime
/// </summary>
private void ReloadLogger()
protected virtual async Task ReloadInternal(IProgress<TaskProgress> progress)
{
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;
await Task.Run(() =>
{
ReportProgress(progress, "Loading Plugins");
ReloadComposableParts();
Logger.LoggerInstance = new TraceLogger();
}).ConfigureAwait(false);
}
/// <summary>
@ -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 = new TConfigurationType();
XmlSerializer.SerializeToFile(Configuration, ApplicationPaths.SystemConfigurationFilePath);
Configuration = XmlSerializer.DeserializeFromFile<TConfigurationType>(ApplicationPaths.SystemConfigurationFilePath);
}
else
catch (FileNotFoundException)
{
Configuration = XmlSerializer.DeserializeFromFile<TConfigurationType>(ApplicationPaths.SystemConfigurationFilePath);
Configuration = new TConfigurationType();
XmlSerializer.SerializeToFile(Configuration, 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();
}
}

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

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

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

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

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

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

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

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

@ -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);
}
if (responseInfo.DateLastModified.HasValue)
{
ctx.Response.Headers[HttpResponseHeader.LastModified] = responseInfo.DateLastModified.Value.ToString("r");
}
// Add caching headers
if (cacheDuration.Ticks > 0)
if (responseInfo.CacheDuration.Ticks > 0)
{
CacheResponse(ctx.Response, cacheDuration, lastDateModified);
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;
}
}
}
}

@ -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()
protected string ContentType
{
switch (SerializationFormat)
get
{
case SerializationFormat.Jsv:
return Task.FromResult("text/plain");
case SerializationFormat.Protobuf:
return Task.FromResult("application/x-protobuf");
default:
return Task.FromResult(MimeTypes.JsonMimeType);
switch (SerializationFormat)
{
case SerializationFormat.Jsv:
return "text/plain";
case SerializationFormat.Protobuf:
return "application/x-protobuf";
default:
return MimeTypes.JsonMimeType;
}
}
}
private bool _objectToSerializeEnsured;
private T _objectToSerialize;
private async Task EnsureObjectToSerialize()
protected override async Task<ResponseInfo> GetResponseInfo()
{
if (!_objectToSerializeEnsured)
ResponseInfo info = new ResponseInfo
{
_objectToSerialize = await GetObjectToSerialize().ConfigureAwait(false);
ContentType = ContentType
};
if (_objectToSerialize == null)
{
StatusCode = 404;
}
_objectToSerialize = await GetObjectToSerialize().ConfigureAwait(false);
_objectToSerializeEnsured = true;
if (_objectToSerialize == null)
{
info.StatusCode = 404;
}
return info;
}
protected abstract Task<T> GetObjectToSerialize();
private T _objectToSerialize;
protected override Task PrepareResponse()
{
return EnsureObjectToSerialize();
}
protected abstract Task<T> GetObjectToSerialize();
protected async override Task WriteResponseToOutputStream(Stream stream)
protected 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;
}
});
}
}

@ -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;
EnsureSourceStream();
ResponseInfo info = new ResponseInfo
{
ContentType = MimeTypes.GetMimeType(Path),
};
if (SourceStream != null)
try
{
SourceStream = File.OpenRead(Path);
}
catch (FileNotFoundException ex)
{
info.StatusCode = 404;
Logger.LogException(ex);
}
catch (DirectoryNotFoundException ex)
{
value = File.GetLastWriteTimeUtc(Path);
info.StatusCode = 404;
Logger.LogException(ex);
}
catch (UnauthorizedAccessException ex)
{
info.StatusCode = 403;
Logger.LogException(ex);
}
return Task.FromResult(value);
}
info.CompressResponse = ShouldCompressResponse(info.ContentType);
public override Task<string> GetContentType()
{
return Task.FromResult(MimeTypes.GetMimeType(Path));
}
if (SourceStream != null)
{
info.DateLastModified = File.GetLastWriteTimeUtc(Path);
}
protected override Task PrepareResponse()
{
EnsureSourceStream();
return Task.FromResult<object>(null);
return Task.FromResult<ResponseInfo>(info);
}
protected override Task WriteResponseToOutputStream(Stream stream)

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading…
Cancel
Save