diff --git a/Marr.Data/LazyLoaded.cs b/Marr.Data/LazyLoaded.cs index e9eb9df31..10d9c13d1 100644 --- a/Marr.Data/LazyLoaded.cs +++ b/Marr.Data/LazyLoaded.cs @@ -1,14 +1,11 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Data.Common; namespace Marr.Data { public interface ILazyLoaded : ICloneable { - void Prepare(Func dbCreator, object parent); + bool IsLoaded { get; } + void Prepare(Func dataMapperFactory, object parent); void LazyLoad(); } @@ -18,8 +15,7 @@ namespace Marr.Data /// public class LazyLoaded : ILazyLoaded { - protected TChild _child; - protected bool _isLoaded; + protected TChild _value; public LazyLoaded() { @@ -27,8 +23,8 @@ namespace Marr.Data public LazyLoaded(TChild val) { - _child = val; - _isLoaded = true; + _value = val; + IsLoaded = true; } public TChild Value @@ -36,11 +32,13 @@ namespace Marr.Data get { LazyLoad(); - return _child; + return _value; } } - public virtual void Prepare(Func dbCreator, object parent) + public bool IsLoaded { get; protected set; } + + public virtual void Prepare(Func dataMapperFactory, object parent) { } public virtual void LazyLoad() @@ -50,7 +48,7 @@ namespace Marr.Data { return new LazyLoaded(val); } - + public static implicit operator TChild(LazyLoaded lazy) { return lazy.Value; @@ -58,7 +56,7 @@ namespace Marr.Data public object Clone() { - return this.MemberwiseClone(); + return MemberwiseClone(); } } @@ -70,7 +68,7 @@ namespace Marr.Data internal class LazyLoaded : LazyLoaded { private TParent _parent; - private Func _dbCreator; + private Func _dbMapperFactory; private readonly Func _query; private readonly Func _condition; @@ -81,46 +79,41 @@ namespace Marr.Data _condition = condition; } - public LazyLoaded(TChild val) + public LazyLoaded(TChild val) : base(val) { - _child = val; - _isLoaded = true; + _value = val; + IsLoaded = true; } /// /// The second part of the initialization happens when the entity is being built. /// - /// Knows how to instantiate a new IDataMapper. + /// Knows how to instantiate a new IDataMapper. /// The parent entity. - public override void Prepare(Func dbCreator, object parent) + public override void Prepare(Func dataMapperFactory, object parent) { - _dbCreator = dbCreator; + _dbMapperFactory = dataMapperFactory; _parent = (TParent)parent; } - public bool IsLoaded - { - get { return _isLoaded; } - } - public override void LazyLoad() { - if (!_isLoaded) + if (!IsLoaded) { if (_condition != null && _condition(_parent)) { - using (IDataMapper db = _dbCreator()) + using (IDataMapper db = _dbMapperFactory()) { - _child = _query(db, _parent); + _value = _query(db, _parent); } } else { - _child = default(TChild); + _value = default(TChild); } - _isLoaded = true; + IsLoaded = true; } } diff --git a/NzbDrone.Api.Test/MappingTests/ResourceMappingFixture.cs b/NzbDrone.Api.Test/MappingTests/ResourceMappingFixture.cs index a453c8643..3df5ec33c 100644 --- a/NzbDrone.Api.Test/MappingTests/ResourceMappingFixture.cs +++ b/NzbDrone.Api.Test/MappingTests/ResourceMappingFixture.cs @@ -1,4 +1,6 @@ using System; +using FluentAssertions; +using Marr.Data; using NUnit.Framework; using NzbDrone.Api.Config; using NzbDrone.Api.Episodes; @@ -40,5 +42,58 @@ namespace NzbDrone.Api.Test.MappingTests MappingValidation.ValidateMapping(modelType, resourceType); } + + [Test] + public void should_map_lay_loaded_values_should_not_be_inject_if_not_loaded() + { + var modelWithLazy = new ModelWithLazy() + { + Guid = new TestLazyLoaded() + }; + + modelWithLazy.InjectTo().Guid.Should().BeEmpty(); + + modelWithLazy.Guid.IsLoaded.Should().BeFalse(); + } + + + [Test] + public void should_map_lay_loaded_values_should_be_inject_if_loaded() + { + + var guid = Guid.NewGuid(); + + var modelWithLazy = new ModelWithLazy() + { + Guid = new LazyLoaded(guid) + }; + + modelWithLazy.InjectTo().Guid.Should().Be(guid); + + modelWithLazy.Guid.IsLoaded.Should().BeTrue(); + } + } + + public class ModelWithLazy + { + public LazyLoaded Guid { get; set; } + } + + public class ModelWithNoLazy + { + public Guid Guid { get; set; } + } + + public class TestLazyLoaded : LazyLoaded + { + public TestLazyLoaded() + { + + } + + public override void Prepare(Func dataMapperFactory, object parent) + { + throw new InvalidOperationException(); + } } } \ No newline at end of file diff --git a/NzbDrone.Api.Test/NzbDrone.Api.Test.csproj b/NzbDrone.Api.Test/NzbDrone.Api.Test.csproj index e8c915e4d..67dccc684 100644 --- a/NzbDrone.Api.Test/NzbDrone.Api.Test.csproj +++ b/NzbDrone.Api.Test/NzbDrone.Api.Test.csproj @@ -73,11 +73,14 @@ - + + {F6FC6BE7-0847-4817-A1ED-223DC647C3D7} + Marr.Data + {FD286DF8-2D3A-4394-8AD5-443FADE55FB2} NzbDrone.Api diff --git a/NzbDrone.Api/Mapping/CloneInjection.cs b/NzbDrone.Api/Mapping/CloneInjection.cs index 6f728833a..0e07e1c04 100644 --- a/NzbDrone.Api/Mapping/CloneInjection.cs +++ b/NzbDrone.Api/Mapping/CloneInjection.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using Marr.Data; using Omu.ValueInjecter; namespace NzbDrone.Api.Mapping @@ -41,22 +42,16 @@ namespace NzbDrone.Api.Mapping if (conventionInfo.SourceProp.Type.IsGenericType) { + var genericInterfaces = conventionInfo.SourceProp.Type.GetGenericTypeDefinition().GetInterfaces(); //handle IEnumerable<> also ICollection<> IList<> List<> - if (conventionInfo.SourceProp.Type.GetGenericTypeDefinition().GetInterfaces().Any(d => d == typeof(IEnumerable))) + if (genericInterfaces.Any(d => d == typeof(IEnumerable))) { - var t = conventionInfo.SourceProp.Type.GetGenericArguments()[0]; - if (t.IsValueType || t == typeof(string)) return conventionInfo.SourceProp.Value; - - var tlist = typeof(List<>).MakeGenericType(t); - var list = Activator.CreateInstance(tlist); + return MapLists(conventionInfo); + } - var addMethod = tlist.GetMethod("Add"); - foreach (var o in (IEnumerable)conventionInfo.SourceProp.Value) - { - var e = Activator.CreateInstance(t).InjectFrom(o); - addMethod.Invoke(list, new[] { e }); // in 4.0 you can use dynamic and just do list.Add(e); - } - return list; + if (genericInterfaces.Any(i => i == typeof(ILazyLoaded))) + { + return MapLazy(conventionInfo); } //unhandled generic type, you could also return null or throw @@ -67,5 +62,38 @@ namespace NzbDrone.Api.Mapping return Activator.CreateInstance(conventionInfo.SourceProp.Type) .InjectFrom(conventionInfo.SourceProp.Value); } + + private static object MapLazy(ConventionInfo conventionInfo) + { + + var genericArgument = conventionInfo.SourceProp.Type.GetGenericArguments()[0]; + + dynamic lazy = conventionInfo.SourceProp.Value; + + if (lazy.IsLoaded && conventionInfo.TargetProp.Type.IsAssignableFrom(genericArgument)) + { + return lazy.Value; + } + + return null; + } + + private static object MapLists(ConventionInfo conventionInfo) + { + var t = conventionInfo.SourceProp.Type.GetGenericArguments()[0]; + if (t.IsValueType || t == typeof(string)) return conventionInfo.SourceProp.Value; + + var tlist = typeof(List<>).MakeGenericType(t); + var list = Activator.CreateInstance(tlist); + + var addMethod = tlist.GetMethod("Add"); + foreach (var o in (IEnumerable)conventionInfo.SourceProp.Value) + { + var e = Activator.CreateInstance(t).InjectFrom(o); + addMethod.Invoke(list, new[] { e }); // in 4.0 you can use dynamic and just do list.Add(e); + } + + return list; + } } } \ No newline at end of file diff --git a/NzbDrone.Api/NzbDrone.Api.csproj b/NzbDrone.Api/NzbDrone.Api.csproj index 50a14ff40..4a2db59b2 100644 --- a/NzbDrone.Api/NzbDrone.Api.csproj +++ b/NzbDrone.Api/NzbDrone.Api.csproj @@ -160,6 +160,10 @@ + + {F6FC6BE7-0847-4817-A1ED-223DC647C3D7} + Marr.Data + {f2be0fdf-6e47-4827-a420-dd4ef82407f8} NzbDrone.Common diff --git a/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj b/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj index 16a8b7a4e..d365c1381 100644 --- a/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj +++ b/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj @@ -89,6 +89,7 @@ + diff --git a/NzbDrone.Api.Test/MappingTests/ReflectionExtensionFixture.cs b/NzbDrone.Common.Test/ReflectionTests/ReflectionExtensionFixture.cs similarity index 90% rename from NzbDrone.Api.Test/MappingTests/ReflectionExtensionFixture.cs rename to NzbDrone.Common.Test/ReflectionTests/ReflectionExtensionFixture.cs index 6d0469b12..4576b24ac 100644 --- a/NzbDrone.Api.Test/MappingTests/ReflectionExtensionFixture.cs +++ b/NzbDrone.Common.Test/ReflectionTests/ReflectionExtensionFixture.cs @@ -1,31 +1,31 @@ -using System.Reflection; -using FluentAssertions; -using NUnit.Framework; -using NzbDrone.Common.Reflection; -using NzbDrone.Core.Datastore; -using NzbDrone.Test.Common; - -namespace NzbDrone.Api.Test.MappingTests -{ - public class ReflectionExtensionFixture : TestBase - { - [Test] - public void should_get_properties_from_models() - { - var models = Assembly.Load("NzbDrone.Core").ImplementationsOf(); - - foreach (var model in models) - { - model.GetSimpleProperties().Should().NotBeEmpty(); - } - } - - [Test] - public void should_be_able_to_get_implementations() - { - var models = Assembly.Load("NzbDrone.Core").ImplementationsOf(); - - models.Should().NotBeEmpty(); - } - } +using System.Reflection; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Common.Reflection; +using NzbDrone.Core.Datastore; +using NzbDrone.Test.Common; + +namespace NzbDrone.Common.Test.ReflectionTests +{ + public class ReflectionExtensionFixture : TestBase + { + [Test] + public void should_get_properties_from_models() + { + var models = Assembly.Load("NzbDrone.Core").ImplementationsOf(); + + foreach (var model in models) + { + model.GetSimpleProperties().Should().NotBeEmpty(); + } + } + + [Test] + public void should_be_able_to_get_implementations() + { + var models = Assembly.Load("NzbDrone.Core").ImplementationsOf(); + + models.Should().NotBeEmpty(); + } + } } \ No newline at end of file diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index bc485a87d..532e2c566 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -404,12 +404,6 @@ Code - - Code - - - Code - Code diff --git a/NzbDrone.Core/Providers/Converting/AtomicParsleyProvider.cs b/NzbDrone.Core/Providers/Converting/AtomicParsleyProvider.cs deleted file mode 100644 index 38d9bad57..000000000 --- a/NzbDrone.Core/Providers/Converting/AtomicParsleyProvider.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using NLog; -using NzbDrone.Core.Configuration; -using NzbDrone.Core.Tv; -using NzbDrone.Core.Model; - - -namespace NzbDrone.Core.Providers.Converting -{ - public class AtomicParsleyProvider - { - private readonly IConfigService _configService; - - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - - public AtomicParsleyProvider(IConfigService configService) - { - _configService = configService; - } - - public AtomicParsleyProvider() - { - - } - - public virtual bool RunAtomicParsley(Episode episode, string outputFile) - { - throw new NotImplementedException(); - - var atomicParsleyLocation = _configService.GetValue("AtomicParsleyLocation", ""); - var atomicParsleyTitleType = (AtomicParsleyTitleType) Convert.ToInt32(_configService.GetValue("AtomicParsley", 0)); - - var atomicParsleyCommand = String.Format("\"{0}\" --overWrite --title \"{1}\" --genre \"TV Shows\" --stik \"TV Show\" --TVShowName \"{2}\" --TVEpisodeNum \"{3}\" --TVSeason \"{4}\"", - outputFile, episode.Title, episode.Series.Title, episode.EpisodeNumber, episode.SeasonNumber); - - //If Episode Number + Name should be in Episode Title (Number - Title) - if (atomicParsleyTitleType == AtomicParsleyTitleType.EpisodeNumber) - { - atomicParsleyCommand = String.Format("\"{0}\" --overWrite --title \"{3} - {1}\" --genre \"TV Shows\" --stik \"TV Show\" --TVShowName \"{2}\" --TVEpisodeNum \"{3}\" --TVSeason \"{4}\"", - outputFile, episode.Title, episode.Series.Title, episode.EpisodeNumber, episode.SeasonNumber); - } - - //If Season/Episode Number + Name should be in Episode Title (SeasonNumber'x'EpisodeNumber - Title) - else if (atomicParsleyTitleType == AtomicParsleyTitleType.Both) - { - atomicParsleyCommand = String.Format("\"{0}\" --overWrite --title \"{4}x{3:00} - {1}\" --genre \"TV Shows\" --stik \"TV Show\" --TVShowName \"{2}\" --TVEpisodeNum \"{3}\" --TVSeason \"{4}\"", - outputFile, episode.Title, episode.Series.Title, episode.EpisodeNumber, episode.SeasonNumber); - } - - try - { - var process = new Process(); - process.StartInfo.FileName = Path.Combine(atomicParsleyLocation, "AtomicParsley.exe"); - process.StartInfo.Arguments = atomicParsleyCommand; - process.StartInfo.UseShellExecute = false; - process.StartInfo.CreateNoWindow = true; - process.StartInfo.RedirectStandardOutput = true; - //process.OutputDataReceived += new DataReceivedEventHandler(HandBrakeOutputDataReceived); - process.Start(); - //process.BeginOutputReadLine(); - process.WaitForExit(); - } - - catch (Exception ex) - { - Logger.DebugException(ex.Message, ex); - return false; - } - - return true; - } - } -} diff --git a/NzbDrone.Core/Providers/Converting/HandbrakeProvider.cs b/NzbDrone.Core/Providers/Converting/HandbrakeProvider.cs deleted file mode 100644 index 6fd7eca30..000000000 --- a/NzbDrone.Core/Providers/Converting/HandbrakeProvider.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System; -using System.Diagnostics; -using System.Text.RegularExpressions; -using NLog; -using NzbDrone.Core.Configuration; -using NzbDrone.Core.Tv; -using NzbDrone.Core.Model.Notification; - - -namespace NzbDrone.Core.Providers.Converting -{ - public class HandbrakeProvider - { - //Interacts with Handbrake - private readonly IConfigService _configService; - private ProgressNotification _notification; - private Episode _currentEpisode; - - private Regex _processingRegex = - new Regex(@"^(?:Encoding).+?(?:\,\s(?\d{1,3}\.\d{2})\s\%)(?:.+?ETA\s(?\d{2})h(?\d{2})m(?\d{2})s)?", - RegexOptions.IgnoreCase | RegexOptions.Compiled); - - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - - public HandbrakeProvider(IConfigService configService) - { - _configService = configService; - } - - public HandbrakeProvider() - { - - } - - public virtual string ConvertFile(Episode episode, ProgressNotification notification) - { - _notification = notification; - _currentEpisode = episode; - - var outputFile = _configService.GetValue("iPodConvertDir", ""); - - var handBrakePreset = _configService.GetValue("HandBrakePreset", "iPhone & iPod Touch"); - var handBrakeCommand = String.Format("-i \"{0}\" -o \"{1}\" --preset=\"{2}\"", episode.EpisodeFile.Value.Path, outputFile, handBrakePreset); - var handBrakeFile = @"C:\Program Files (x86)\Handbrake\HandBrakeCLI.exe"; - - try - { - var process = new Process(); - process.StartInfo.FileName = handBrakeFile; - process.StartInfo.Arguments = handBrakeCommand; - process.StartInfo.UseShellExecute = false; - process.StartInfo.CreateNoWindow = true; - process.StartInfo.RedirectStandardOutput = true; - process.OutputDataReceived += new DataReceivedEventHandler(HandBrakeOutputDataReceived); - process.Start(); - process.BeginOutputReadLine(); - process.WaitForExit(); - } - - catch (Exception ex) - { - Logger.DebugException(ex.Message, ex); - return String.Empty; - } - - return outputFile; - } - - private void HandBrakeOutputDataReceived(object obj, DataReceivedEventArgs args) - { - throw new NotImplementedException(); - - //args.Data contains the line writen - - var match = _processingRegex.Matches(args.Data); - - if (match.Count != 1) - return; - - var episodeString = String.Format("{0} - {1}x{2:00}", - _currentEpisode.Series.Title, - _currentEpisode.SeasonNumber, - _currentEpisode.EpisodeNumber); - - var percent = Convert.ToDecimal(match[0].Groups["percent"].Value); - int hours; - int minutes; - int seconds; - - Int32.TryParse(match[0].Groups["hours"].Value, out hours); - Int32.TryParse(match[0].Groups["minutes"].Value, out minutes); - Int32.TryParse(match[0].Groups["seconds"].Value, out seconds); - - if (seconds > 0 || minutes > 0 || hours > 0) - { - var eta = DateTime.Now.Add(new TimeSpan(0, hours, minutes, seconds)); - _notification.CurrentMessage = String.Format("Converting: {0}, {1}%. ETA: {2}", episodeString, percent, eta); - } - - else - _notification.CurrentMessage = String.Format("Converting: {0}, {1}%.", episodeString, percent); - - Console.WriteLine(args.Data); - } - } -} diff --git a/NzbDrone.Core/Providers/MisnamedProvider.cs b/NzbDrone.Core/Providers/MisnamedProvider.cs index fb20010c1..267ee76d7 100644 --- a/NzbDrone.Core/Providers/MisnamedProvider.cs +++ b/NzbDrone.Core/Providers/MisnamedProvider.cs @@ -50,8 +50,7 @@ namespace NzbDrone.Core.Providers CurrentName = currentName, EpisodeFileId = firstEpisode.EpisodeFileId, ProperName = properName, - SeriesId = firstEpisode.SeriesId, - SeriesTitle = firstEpisode.Series.Title + SeriesTitle = firstEpisode.Series.Value.Title }); } }); diff --git a/NzbDrone.Core/Tv/Episode.cs b/NzbDrone.Core/Tv/Episode.cs index 1fc1be20f..3a0f5e13a 100644 --- a/NzbDrone.Core/Tv/Episode.cs +++ b/NzbDrone.Core/Tv/Episode.cs @@ -30,15 +30,15 @@ namespace NzbDrone.Core.Tv public String SeriesTitle { get; private set; } - public Series Series { get; set; } + public LazyLoaded Series { get; set; } public LazyLoaded EpisodeFile { get; set; } public override string ToString() { - string seriesTitle = Series == null ? "[NULL]" : Series.Title; + string seriesTitle = Series == null ? "[NULL]" : Series.Value.Title; - if (Series != null && Series.SeriesType == SeriesTypes.Daily && AirDate.HasValue) + if (Series != null && Series.Value.SeriesType == SeriesTypes.Daily && AirDate.HasValue) return string.Format("{0} - {1:yyyy-MM-dd}", seriesTitle, AirDate.Value); return string.Format("{0} - S{1:00}E{2:00}", seriesTitle, SeasonNumber, EpisodeNumber); diff --git a/NzbDrone.Core/Tv/EpisodeService.cs b/NzbDrone.Core/Tv/EpisodeService.cs index 3bfd7e55c..e287a0edb 100644 --- a/NzbDrone.Core/Tv/EpisodeService.cs +++ b/NzbDrone.Core/Tv/EpisodeService.cs @@ -208,7 +208,7 @@ namespace NzbDrone.Core.Tv int episodeCount = 0; foreach (var episode in group.OrderBy(e => e.SeasonNumber).ThenBy(e => e.EpisodeNumber)) { - episode.AirDate = episode.AirDate.Value.AddMinutes(episode.Series.Runtime * episodeCount); + episode.AirDate = episode.AirDate.Value.AddMinutes(episode.Series.Value.Runtime * episodeCount); episodeCount++; } }