Added basic episode support

This commit is contained in:
Keivan 2010-10-04 23:21:18 -07:00
parent 606140832d
commit 81e155ae42
33 changed files with 458 additions and 290 deletions

View File

@ -26,7 +26,7 @@ namespace NzbDrone.Core.Test
var repo = new Mock<IRepository>();
var config = new Config { Key = key, Value = value };
repo.Setup(r => r.Single<Config>(key)).Returns(config);
var target = new ConfigProvider(new Mock<ILog>().Object, repo.Object);
var target = new ConfigProvider(repo.Object);
//Act
target.SetValue(key, value);
@ -45,7 +45,7 @@ namespace NzbDrone.Core.Test
//Arrange
var repo = new Mock<IRepository>();
repo.Setup(r => r.Single<Config>(It.IsAny<string>())).Returns<Config>(null).Verifiable();
var target = new ConfigProvider(new Mock<ILog>().Object, repo.Object);
var target = new ConfigProvider(repo.Object);
//Act
target.SetValue(key, value);

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Text;
using Gallio.Framework;
using MbUnit.Framework;
using MbUnit.Framework.ContractVerifiers;
namespace NzbDrone.Core.Test
{
[TestFixture]
public class EpisodeProviderTest
{
}
}

View File

@ -27,7 +27,7 @@ namespace NzbDrone.Core.Test
[SetUp]
public void Setup()
{
Main.ConfigureNlog();
CentralDispatch.ConfigureNlog();
}
}
}

View File

@ -7,6 +7,7 @@ using Moq;
using NzbDrone.Core.Providers;
using SubSonic.DataProviders;
using SubSonic.Repository;
using TvdbLib;
namespace NzbDrone.Core.Test
{
@ -17,12 +18,9 @@ namespace NzbDrone.Core.Test
{
public static string[] StandardSeries
{
get { return new string[] { "C:\\TV\\The Simpsons", "C:\\TV\\Family Guy" }; }
get { return new string[] { "c:\\tv\\the simpsons", "c:\\tv\\family guy", "c:\\tv\\southpark", "c:\\tv\\24" }; }
}
public static IRepository GetEmptyRepository()
{
var provider = ProviderFactory.GetProvider("Data Source=" + Guid.NewGuid() + ".testdb;Version=3;New=True", "System.Data.SQLite");
@ -39,9 +37,7 @@ namespace NzbDrone.Core.Test
}
}
public static IDiskProvider StandardDisk
{
get
public static IDiskProvider GetStandardDisk()
{
var mock = new Mock<IDiskProvider>();
mock.Setup(c => c.GetDirectories(It.IsAny<String>())).Returns(StandardSeries);
@ -50,4 +46,3 @@ namespace NzbDrone.Core.Test
}
}
}
}

View File

@ -41,7 +41,6 @@
<HintPath>..\NzbDrone.Core\Libraries\log4net.dll</HintPath>
</Reference>
<Reference Include="MbUnit, Version=3.2.0.0, Culture=neutral, PublicKeyToken=eb9cfa67ee6ab36e, processorArchitecture=MSIL" />
<Reference Include="MbUnit35, Version=3.2.0.0, Culture=neutral, PublicKeyToken=eb9cfa67ee6ab36e, processorArchitecture=MSIL" />
<Reference Include="Moq, Version=4.0.10827.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>Moq\Moq.dll</HintPath>
@ -55,13 +54,11 @@
<HintPath>..\NzbDrone.Core\Libraries\SubSonic.Core.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data.SQLite">
<HintPath>Libs\System.Data.SQLite.DLL</HintPath>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Data.SQLite, Version=1.0.66.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=AMD64">
<HintPath>Libs\System.Data.SQLite.DLL</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Xml" />
<Reference Include="TvdbLib">
<HintPath>..\NzbDrone.Core\Libraries\TvdbLib.dll</HintPath>
@ -69,6 +66,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="DbConfigControllerTest.cs" />
<Compile Include="EpisodeProviderTest.cs" />
<Compile Include="Fixtures.cs" />
<Compile Include="MockLib.cs" />
<Compile Include="Ninject.Moq\ExtensionsForBindingSyntax.cs" />
@ -77,7 +75,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="QualityProfileTest.cs" />
<Compile Include="SabControllerTest.cs" />
<Compile Include="SeriesTest.cs" />
<Compile Include="SeriesProviderTest.cs" />
<Compile Include="TvDbControllerTest.cs" />
</ItemGroup>
<ItemGroup>
@ -99,7 +97,9 @@
<Content Include="Files\QueueError.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Libs\System.Data.SQLite.DLL" />
<Content Include="Libs\System.Data.SQLite.DLL">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<None Include="App.config">

View File

@ -36,7 +36,7 @@ namespace NzbDrone.Core.Test
var http = new Mock<IHttpProvider>();
http.Setup(s => s.DownloadString("http://192.168.5.55:2222/sabnzbd/api?mode=addurl&name=http://www.nzbclub.com/nzb_download.aspx?mid=1950232&priority=0&cat=tv&nzbname=This+is+an+Nzb&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass")).Returns("ok");
var target = new SabProvider(config.Object, new Mock<ILog>().Object, http.Object);
var target = new SabProvider(config.Object, http.Object);
//Act
bool result = target.AddByUrl("http://www.nzbclub.com/nzb_download.aspx?mid=1950232", "This is an Nzb");
@ -65,7 +65,7 @@ namespace NzbDrone.Core.Test
var http = new Mock<IHttpProvider>();
http.Setup(s => s.DownloadString("http://192.168.5.55:2222/sabnzbd/api?mode=addurl&name=http://www.nzbclub.com/nzb_download.aspx?mid=1950232&priority=0&cat=tv&nzbname=This+is+an+Nzb&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass")).Returns("error");
var target = new SabProvider(config.Object, new Mock<ILog>().Object, http.Object);
var target = new SabProvider(config.Object, http.Object);
//Act
bool result = target.AddByUrl("http://www.nzbclub.com/nzb_download.aspx?mid=1950232", "This is an Nzb");
@ -98,7 +98,7 @@ namespace NzbDrone.Core.Test
"http://192.168.5.55:2222/sabnzbd/api?mode=queue&output=xml&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"))
.Returns(new StreamReader(@".\Files\Queue.xml").ReadToEnd());
var target = new SabProvider(config.Object, new Mock<ILog>().Object, http.Object);
var target = new SabProvider(config.Object, http.Object);
//Act
bool result = target.IsInQueue("Ubuntu Test");
@ -131,7 +131,7 @@ namespace NzbDrone.Core.Test
"http://192.168.5.55:2222/sabnzbd/api?mode=queue&output=xml&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"))
.Returns(new StreamReader(@".\Files\QueueEmpty.xml").ReadToEnd());
var target = new SabProvider(config.Object, new Mock<ILog>().Object, http.Object);
var target = new SabProvider(config.Object, http.Object);
//Act
bool result = target.IsInQueue(String.Empty);
@ -164,7 +164,7 @@ namespace NzbDrone.Core.Test
"http://192.168.5.55:2222/sabnzbd/api?mode=queue&output=xml&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"))
.Returns(new StreamReader(@".\Files\QueueError.xml").ReadToEnd());
var target = new SabProvider(config.Object, new Mock<ILog>().Object, http.Object);
var target = new SabProvider(config.Object, http.Object);
//Act
bool result = target.IsInQueue(String.Empty);

View File

@ -12,6 +12,7 @@ using Ninject;
using Ninject.Moq;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Episode;
using SubSonic.Repository;
using TvdbLib.Data;
using System.Linq;
@ -20,7 +21,7 @@ using System.Linq;
namespace NzbDrone.Core.Test
{
[TestFixture]
public class SeriesTest
public class SeriesProviderTest
{
[Test]
public void Map_path_to_series()
@ -34,10 +35,10 @@ namespace NzbDrone.Core.Test
var moqData = new Mock<IRepository>();
var moqTvdb = new Mock<ITvDbProvider>();
moqData.Setup(f => f.Exists<Series>(c => c.TvdbId == It.IsAny<long>())).Returns(false);
moqData.Setup(f => f.Exists<Series>(c => c.SeriesId == It.IsAny<int>())).Returns(false);
moqTvdb.Setup(f => f.GetSeries(It.IsAny<String>())).Returns(fakeSearch);
moqTvdb.Setup(f => f.GetSeries(fakeSeries.Id, It.IsAny<TvdbLanguage>())).Returns(fakeSeries);
moqTvdb.Setup(f => f.GetSeries(fakeSeries.Id, false)).Returns(fakeSeries).Verifiable();
var kernel = new MockingKernel();
kernel.Bind<IRepository>().ToConstant(moqData.Object);
@ -49,6 +50,7 @@ namespace NzbDrone.Core.Test
var mappedSeries = seriesController.MapPathToSeries(@"D:\TV Shows\The Simpsons");
//Assert
moqTvdb.VerifyAll();
Assert.AreEqual(fakeSeries, mappedSeries);
}
@ -71,7 +73,7 @@ namespace NzbDrone.Core.Test
{
//Arrange
var sonicRepo = MockLib.GetEmptyRepository();
var series = Builder<Series>.CreateNew().With(c => c.TvdbId = tvdbId).Build();
var series = Builder<Series>.CreateNew().With(c => c.SeriesId = tvdbId).Build();
//Act
var addId = sonicRepo.Add(series);
@ -80,28 +82,43 @@ namespace NzbDrone.Core.Test
Assert.AreEqual(tvdbId, addId);
var allSeries = sonicRepo.All<Series>();
Assert.IsNotEmpty(allSeries);
Assert.AreEqual(tvdbId, allSeries.First().TvdbId);
Assert.AreEqual(tvdbId, allSeries.First().SeriesId);
}
[Test]
[Row(new object[] { "CAPITAL", "capital", true })]
[Row(new object[] { "Something!!", "Something", true })]
[Row(new object[] { "Simpsons 2000", "Simpsons", true })]
[Row(new object[] { "Simp222sons", "Simpsons", true })]
[Row(new object[] { "Simpsons", "The Simpsons", true })]
[Row(new object[] { "Law and order", "Law & order", true })]
[Row(new object[] { "xxAndxx", "xxxx", false })]
[Row(new object[] { "Andxx", "xx", false })]
[Row(new object[] { "xxAnd", "xx", false })]
[Row(new object[] { "Thexx", "xx", false })]
[Row(new object[] { "Thexx", "xx", false })]
[Row(new object[] { "xxThexx", "xxxxx", false })]
[Row(new object[] { "Simpsons The", "Simpsons", true })]
public void Name_match_test(string a, string b, bool match)
public void get_unmapped()
{
bool result = TvDbProvider.IsTitleMatch(a, b);
//Setup
var kernel = new MockingKernel();
Assert.AreEqual(match, result, "{0} , {1}", a, b);
kernel.Bind<ISeriesProvider>().To<SeriesProvider>();
kernel.Bind<IDiskProvider>().ToConstant(MockLib.GetStandardDisk());
var seriesController = kernel.Get<ISeriesProvider>();
//Act
var unmappedFolder = seriesController.GetUnmappedFolders();
//Assert
Assert.AreElementsEqualIgnoringOrder(MockLib.StandardSeries, unmappedFolder);
}
[Test]
public void get_episode_test()
{
var fakeSeries = Builder<Series>.CreateNew().Build();
var fakeEpisode = Builder<EpisodeInfo>.CreateNew().With(c => c.SeriesId).Build();
Console.WriteLine("test");
var repo = MockLib.GetEmptyRepository();
repo.Add(fakeSeries);
repo.Add(fakeEpisode);
var fetchedSeries = repo.Single<Series>(fakeSeries.SeriesId);
Assert.IsNotEmpty(fetchedSeries.Episodes);
}
}
}

View File

@ -15,14 +15,77 @@ namespace NzbDrone.Core.Test
[Row("The Simpsons")]
[Row("Family Guy")]
[Row("South Park")]
[Ignore("Have to find a way to mock app path for tests.")]
public void TestSearch(string title)
[Row("clone high, usa")]
public void successful_search(string title)
{
var tvCont = new TvDbProvider();
var result = tvCont.SearchSeries(title);
Assert.AreNotEqual(0, result.Count);
Assert.IsNotEmpty(result);
Assert.AreEqual(title, result[0].SeriesName, StringComparison.InvariantCultureIgnoreCase);
}
[Test]
[Row("The Simpsons")]
[Row("Family Guy")]
[Row("South Park")]
public void successful_title_lookup(string title)
{
var tvCont = new TvDbProvider();
var result = tvCont.GetSeries(title);
Assert.AreEqual(title, result.SeriesName, StringComparison.InvariantCultureIgnoreCase);
}
[Test]
[Row(new object[] { "CAPITAL", "capital", true })]
[Row(new object[] { "Something!!", "Something", true })]
[Row(new object[] { "Simpsons 2000", "Simpsons", true })]
[Row(new object[] { "Simp222sons", "Simpsons", true })]
[Row(new object[] { "Simpsons", "The Simpsons", true })]
[Row(new object[] { "Law and order", "Law & order", true })]
[Row(new object[] { "xxAndxx", "xxxx", false })]
[Row(new object[] { "Andxx", "xx", false })]
[Row(new object[] { "xxAnd", "xx", false })]
[Row(new object[] { "Thexx", "xx", false })]
[Row(new object[] { "Thexx", "xx", false })]
[Row(new object[] { "xxThexx", "xxxxx", false })]
[Row(new object[] { "Simpsons The", "Simpsons", true })]
public void Name_match_test(string a, string b, bool match)
{
bool result = TvDbProvider.IsTitleMatch(a, b);
Assert.AreEqual(match, result, "{0} , {1}", a, b);
}
[Test]
public void no_search_result()
{
//setup
var tvdbProvider = new TvDbProvider();
//act
var result = tvdbProvider.SearchSeries("clone high");
//assert
Assert.IsEmpty(result);
}
[Test]
public void no_result_title_lookup()
{
//setup
var tvdbProvider = new TvDbProvider();
//act
var result = tvdbProvider.GetSeries("clone high");
//assert
Assert.IsNull(result);
}
}
}

View File

@ -5,13 +5,15 @@ using Ninject;
using NLog.Config;
using NLog.Targets;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Episode;
using SubSonic.DataProviders;
using SubSonic.Repository;
using NLog;
namespace NzbDrone.Core
{
public static class Main
public static class CentralDispatch
{
public static void BindKernel(IKernel kernel)
{
@ -19,16 +21,27 @@ namespace NzbDrone.Core
var provider = ProviderFactory.GetProvider(connectionString, "System.Data.SQLite");
kernel.Bind<ISeriesProvider>().To<SeriesProvider>();
kernel.Bind<ISeasonProvider>().To<SeasonProvider>();
kernel.Bind<IEpisodeProvider>().To<EpisodeProvider>();
kernel.Bind<IDiskProvider>().To<DiskProvider>();
kernel.Bind<ITvDbProvider>().To<TvDbProvider>();
kernel.Bind<IConfigProvider>().To<ConfigProvider>();
kernel.Bind<log4net.ILog>().ToMethod(c => log4net.LogManager.GetLogger("logger-name"));
kernel.Bind<IRepository>().ToMethod(c => new SimpleRepository(provider, SimpleRepositoryOptions.RunMigrations));
kernel.Bind<IConfigProvider>().To<ConfigProvider>().InSingletonScope();
kernel.Bind<IRepository>().ToMethod(c => new SimpleRepository(provider, SimpleRepositoryOptions.RunMigrations)).InSingletonScope();
ForceMigration(kernel.Get<IRepository>());
}
public static String AppPath
{
get { return new DirectoryInfo(HttpContext.Current.Server.MapPath("\\")).FullName; }
get
{
if (HttpContext.Current != null)
{
return new DirectoryInfo(HttpContext.Current.Server.MapPath("\\")).FullName;
}
return Directory.GetCurrentDirectory();
}
}
@ -67,5 +80,12 @@ namespace NzbDrone.Core
logger.Error("error log message");
logger.Fatal("fatal log message");
}
private static void ForceMigration(IRepository repository)
{
repository.GetPaged<Series>(0, 1);
repository.GetPaged<EpisodeInfo>(0, 1);
repository.GetPaged<Series>(0, 1);
}
}
}

View File

@ -125,7 +125,6 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>Libraries\Castle.Core.dll</HintPath>
</Reference>
<Reference Include="log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821, processorArchitecture=MSIL" />
<Reference Include="Ninject, Version=2.0.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7, processorArchitecture=MSIL" />
<Reference Include="NLog, Version=2.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL" />
<Reference Include="NLog.Extended, Version=2.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
@ -157,16 +156,17 @@
<Compile Include="Providers\ISeriesProvider.cs" />
<Compile Include="Providers\ITvDbProvider.cs" />
<Compile Include="Providers\SabProvider.cs" />
<Compile Include="Providers\SeasonProvider.cs" />
<Compile Include="Repository\Episode\RemoteEpisode.cs" />
<Compile Include="Repository\Episode\EpisodeInfo.cs" />
<Compile Include="Repository\Quality\AllowedQuality.cs" />
<Compile Include="Repository\Config.cs" />
<Compile Include="Repository\Quality\QualityProfile.cs" />
<Compile Include="Repository\Season.cs" />
<Compile Include="Repository\RemoteEpisode.cs" />
<Compile Include="Repository\LocalEpisode.cs" />
<Compile Include="Repository\Episode.cs" />
<Compile Include="Repository\Episode\Episode.cs" />
<Compile Include="Repository\Quality\QualityTypes.cs" />
<Compile Include="Repository\Series.cs" />
<Compile Include="Main.cs" />
<Compile Include="CentralDispatch.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Providers\DiskProvider.cs" />
<Compile Include="Providers\IConfigProvider.cs" />

View File

@ -1,5 +1,5 @@
using System;
using log4net;
using NLog;
using NzbDrone.Core.Repository;
using SubSonic.Repository;
@ -8,13 +8,11 @@ namespace NzbDrone.Core.Providers
public class ConfigProvider : IConfigProvider
{
private const string SERIES_ROOTS = "SeriesRoots";
private readonly ILog _logger;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly IRepository _sonicRepo;
public ConfigProvider(ILog logger, IRepository dataRepository)
public ConfigProvider(IRepository dataRepository)
{
_logger = logger;
_sonicRepo = dataRepository;
}
@ -39,7 +37,7 @@ namespace NzbDrone.Core.Providers
if (dbValue != null && !String.IsNullOrEmpty(dbValue.Value))
return dbValue.Value;
_logger.WarnFormat("Unable to find config key '{0}' defaultValue:'{1}'", key, defaultValue);
Logger.Debug("Unable to find config key '{0}' defaultValue:'{1}'", key, defaultValue);
if (makePermanent)
SetValue(key, defaultValue.ToString());
value = defaultValue.ToString();
@ -54,13 +52,14 @@ namespace NzbDrone.Core.Providers
if (value == null)
throw new ArgumentNullException("key");
_logger.DebugFormat("Writing Setting to file. Key:'{0}' Value:'{1}'", key, value);
Logger.Debug("Writing Setting to file. Key:'{0}' Value:'{1}'", key, value);
var dbValue = _sonicRepo.Single<Config>(key);
if (dbValue == null)
{
_sonicRepo.Add(new Config {
_sonicRepo.Add(new Config
{
Key = key,
Value = value
});

View File

@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using NLog;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Episode;
using NzbDrone.Core.Repository.Quality;
using SubSonic.Repository;
@ -27,12 +29,19 @@ namespace NzbDrone.Core.Providers
private readonly IRepository _sonicRepo;
private readonly ISeriesProvider _seriesProvider;
private readonly ISeriesProvider _series;
private readonly ISeasonProvider _seasons;
private readonly ITvDbProvider _tvDb;
private static readonly Logger Logger = NLog.LogManager.GetCurrentClassLogger();
public EpisodeProvider(IRepository sonicRepo, ISeriesProvider seriesProvider)
public EpisodeProvider(IRepository sonicRepo, ISeriesProvider seriesProvider, ISeasonProvider seasonProvider, ITvDbProvider tvDbProvider)
{
_sonicRepo = sonicRepo;
_seriesProvider = seriesProvider;
_series = seriesProvider;
_tvDb = tvDbProvider;
_seasons = seasonProvider;
}
public Episode GetEpisode(long id)
@ -40,7 +49,7 @@ namespace NzbDrone.Core.Providers
throw new NotImplementedException();
}
public Episode SaveEpisode(Episode episode)
public Episode UpdateEpisode(Episode episode)
{
throw new NotImplementedException();
}
@ -57,7 +66,7 @@ namespace NzbDrone.Core.Providers
public String GetSabTitle(Episode episode)
{
var series = _seriesProvider.GetSeries(episode.SeriesId);
var series = _series.GetSeries(episode.SeriesId);
if (series == null) throw new ArgumentException("Unknown series. ID: " + episode.SeriesId);
//TODO: This method should return a standard title for the sab episode.
@ -75,6 +84,44 @@ namespace NzbDrone.Core.Providers
throw new NotImplementedException();
}
public void RefreshSeries(int seriesId)
{
Logger.Info("Starting episode info refresh for series:{0}", seriesId);
int successCount = 0;
int failCount = 0;
var targetSeries = _tvDb.GetSeries(seriesId, true);
foreach (var episode in targetSeries.Episodes)
{
try
{
_seasons.EnsureSeason(seriesId, episode.SeasonId, episode.SeasonNumber);
var newEpisode = new EpisodeInfo()
{
AirDate = episode.FirstAired,
EpisodeId = episode.Id,
EpisodeNumber = episode.EpisodeNumber,
Language = episode.Language.Abbriviation,
Overview = episode.Overview,
SeasonId = episode.SeasonId,
SeasonNumber = episode.SeasonNumber,
SeriesId = episode.SeriesId,
Title = episode.EpisodeName
};
_sonicRepo.Add<EpisodeInfo>(newEpisode);
successCount++;
}
catch (Exception e)
{
Logger.FatalException(String.Format("An error has occured while updating episode info for series {0}", seriesId), e);
failCount++;
}
}
Logger.Info("Finished episode refresh for series:{0}. Success:{1} - Fail:{2} ", seriesId, successCount, failCount);
}
/// <summary>
@ -82,27 +129,25 @@ namespace NzbDrone.Core.Providers
/// </summary>
/// <param name="title">Title of the report</param>
/// <returns>List of episodes relating to the post</returns>
public static List<Episode> Parse(string title)
public static List<RemoteEpisode> Parse(string title)
{
var match = ParseRegex.Match(title);
if (!match.Success)
throw new ArgumentException(String.Format("Title doesn't match any know patterns. [{0}]", title));
var result = new List<Episode>();
var result = new List<RemoteEpisode>();
result.Add(new Episode() { EpisodeNumber = Convert.ToInt32(match.Groups["episodeNumber"].Value) });
result.Add(new RemoteEpisode { EpisodeNumber = Convert.ToInt32(match.Groups["episodeNumber"].Value) });
if (match.Groups["episodeNumber2"].Success)
{
result.Add(new Episode() { EpisodeNumber = Convert.ToInt32(match.Groups["episodeNumber2"].Value) });
result.Add(new RemoteEpisode { EpisodeNumber = Convert.ToInt32(match.Groups["episodeNumber2"].Value) });
}
foreach (var ep in result)
{
//TODO: Get TVDB episode Title, Series name and the rest of the details
ep.SeasonNumber = Convert.ToInt32(match.Groups["seasonNumber"].Value);
ep.Title = ReplaceSeparatorChars(match.Groups["episodeName"].Value);
ep.Proper = title.Contains("PROPER");
ep.Quality = QualityTypes.Unknown;
}
@ -110,13 +155,5 @@ namespace NzbDrone.Core.Providers
return result;
}
private static string ReplaceSeparatorChars(string text)
{
if (text == null)
throw new ArgumentNullException("text");
return text.Replace('.', ' ').Replace('-', ' ').Replace('_', ' ').Trim();
}
}
}

View File

@ -1,13 +1,14 @@
using System;
using System.Collections.Generic;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Episode;
namespace NzbDrone.Core.Providers
{
public interface IEpisodeProvider
{
Episode GetEpisode(long id);
Episode SaveEpisode(Episode episode);
Episode UpdateEpisode(Episode episode);
IList<Episode> GetEpisodesBySeason(long seasonId);
IList<Episode> GetEpisodeBySeries(long seriesId);
String GetSabTitle(Episode episode);
@ -18,5 +19,7 @@ namespace NzbDrone.Core.Providers
/// <param name="episode">Episode that needs to be checked</param>
/// <returns></returns>
bool IsNeeded(Episode episode);
void RefreshSeries(int seriesId);
}
}

View File

@ -5,10 +5,9 @@ namespace NzbDrone.Core.Providers
{
public interface ISeasonProvider
{
Season GetSeason(long seasonId);
List<Season> GetSeasongs(long seriesId);
Season GetSeason(int seasonId);
List<Season> GetSeasongs(int seriesId);
void EnsureSeason(int seriesId, int seasonId, int seasonNumber);
int SaveSeason(Season season);
}
}

View File

@ -9,7 +9,7 @@ namespace NzbDrone.Core.Providers
public interface ISeriesProvider
{
IQueryable<Series> GetSeries();
Series GetSeries(long tvdbId);
Series GetSeries(int seriesId);
void SyncSeriesWithDisk();
/// <summary>

View File

@ -6,7 +6,7 @@ namespace NzbDrone.Core.Providers
public interface ITvDbProvider
{
IList<TvdbSearchResult> SearchSeries(string name);
TvdbSeries GetSeries(int id, TvdbLanguage language);
TvdbSearchResult GetSeries(string title);
TvdbSeries GetSeries(int id, bool loadEpisodes);
}
}

View File

@ -2,20 +2,20 @@
using System.Linq;
using System.Web;
using System.Xml.Linq;
using log4net;
using NLog;
namespace NzbDrone.Core.Providers
{
public class SabProvider : IDownloadProvider
{
private readonly IConfigProvider _config;
private readonly ILog _logger;
private readonly IHttpProvider _http;
public SabProvider(IConfigProvider config, ILog logger, IHttpProvider http)
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
public SabProvider(IConfigProvider config, IHttpProvider http)
{
_config = config;
_logger = logger;
_http = http;
}
@ -32,10 +32,10 @@ namespace NzbDrone.Core.Providers
string action = string.Format("mode={0}&name={1}&priority={2}&cat={3}&nzbname={4}", mode, name, priority, cat, nzbName);
string request = GetSabRequest(action);
_logger.DebugFormat("Adding report [{0}] to the queue.", nzbName);
Logger.Debug("Adding report [{0}] to the queue.", nzbName);
string response = _http.DownloadString(request).Replace("\n", String.Empty);
_logger.DebugFormat("Queue Repsonse: [{0}]", response);
Logger.Debug("Queue Repsonse: [{0}]", response);
if (response == "ok")
return true;
@ -61,7 +61,7 @@ namespace NzbDrone.Core.Providers
//Get the Count of Items in Queue where 'filename' is Equal to goodName, if not zero, return true (isInQueue)))
if ((xDoc.Descendants("slot").Where(s => s.Element("filename").Value.Equals(title, StringComparison.InvariantCultureIgnoreCase))).Count() != 0)
{
_logger.DebugFormat("Episode in queue - '{0}'", title);
Logger.Debug("Episode in queue - '{0}'", title);
return true;
}

View File

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using NLog;
using NzbDrone.Core.Repository;
using SubSonic.Repository;
namespace NzbDrone.Core.Providers
{
class SeasonProvider : ISeasonProvider
{
private readonly IRepository _sonicRepo;
private static readonly Logger Logger = NLog.LogManager.GetCurrentClassLogger();
public SeasonProvider(IRepository dataRepository)
{
_sonicRepo = dataRepository;
}
public Season GetSeason(int seasonId)
{
throw new NotImplementedException();
}
public List<Season> GetSeasongs(int seriesId)
{
throw new NotImplementedException();
}
public void EnsureSeason(int seriesId, int seasonId, int seasonNumber)
{
if (_sonicRepo.Exists<Season>(s => s.SeasonId == seasonId))
return;
//TODO: Calculate Season Folder
Logger.Debug("Creating Season. SeriesID:{0} SeasonID:{1} SeasonNumber:{2} Folder:{3}", seriesId, seasonId, seasonNumber, string.Empty);
var newSeason = new Season()
{
Folder = String.Empty,
Monitored = true,
SeasonId = seasonId,
SeasonNumber = seasonNumber,
SeriesId = seriesId
};
_sonicRepo.Add<Season>(newSeason);
}
public int SaveSeason(Season season)
{
throw new NotImplementedException();
}
}
}

View File

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using log4net;
using NLog;
using NzbDrone.Core.Repository;
using SubSonic.Repository;
@ -38,7 +37,7 @@ namespace NzbDrone.Core.Providers
private readonly IDiskProvider _diskProvider;
private readonly IRepository _sonioRepo;
private readonly ITvDbProvider _tvDb;
private static readonly Logger Logger = NLog.LogManager.GetCurrentClassLogger();
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private static readonly Regex CleanUpRegex = new Regex(@"((\s|^)the(\s|$))|((\s|^)and(\s|$))|[^a-z]", RegexOptions.IgnoreCase | RegexOptions.Compiled);
public SeriesProvider(IDiskProvider diskProvider, IConfigProvider configProvider, IRepository dataRepository, ITvDbProvider tvDbProvider)
@ -56,9 +55,9 @@ namespace NzbDrone.Core.Providers
return _sonioRepo.All<Series>();
}
public Series GetSeries(long tvdbId)
public Series GetSeries(int seriesId)
{
return _sonioRepo.Single<Series>(s => s.TvdbId == tvdbId);
return _sonioRepo.Single<Series>(s => s.SeriesId == seriesId);
}
/// <summary>
@ -68,7 +67,7 @@ namespace NzbDrone.Core.Providers
/// <returns>Whether or not the show is monitored</returns>
public bool IsMonitored(long id)
{
return _sonioRepo.Exists<Series>(c => c.TvdbId == id && c.Monitored);
return _sonioRepo.Exists<Series>(c => c.SeriesId == id && c.Monitored);
}
/// <summary>
@ -99,10 +98,11 @@ namespace NzbDrone.Core.Providers
if (mappedSeries == null)
{
Logger.Warn("Unable to find a matching series for '{0}'", seriesFolder);
break;
}
else
{
if (!_sonioRepo.Exists<Series>(s => s.TvdbId == mappedSeries.Id))
if (!_sonioRepo.Exists<Series>(s => s.SeriesId == mappedSeries.Id))
{
RegisterSeries(seriesFolder, mappedSeries);
}
@ -110,6 +110,7 @@ namespace NzbDrone.Core.Providers
{
Logger.Warn("Folder '{0}' mapped to '{1}' which is already another folder assigned to it.'", seriesFolder, mappedSeries.SeriesName);
}
}
}
}
@ -137,7 +138,7 @@ namespace NzbDrone.Core.Providers
if (searchResults == null)
return null;
return _tvDb.GetSeries(searchResults.Id, searchResults.Language);
return _tvDb.GetSeries(searchResults.Id, false);
}
@ -145,7 +146,7 @@ namespace NzbDrone.Core.Providers
{
Logger.Info("registering '{0}' with [{1}]-{2}", path, series.Id, series.SeriesName);
var repoSeries = new Series();
repoSeries.TvdbId = series.Id;
repoSeries.SeriesId = series.Id;
repoSeries.Title = series.SeriesName;
repoSeries.AirTimes = series.AirsTime;
repoSeries.AirsDayOfWeek = series.AirsDayOfWeek;

View File

@ -19,7 +19,7 @@ namespace NzbDrone.Core.Providers
public TvDbProvider()
{
_handler = new TvdbHandler(new XmlCacheProvider(Path.Combine(Main.AppPath, @"cache\tvdbcache.xml")), TVDB_APIKEY);
_handler = new TvdbHandler(new XmlCacheProvider(CentralDispatch.AppPath + @"\cache\tvdb"), TVDB_APIKEY);
}
#region ITvDbProvider Members
@ -44,13 +44,17 @@ namespace NzbDrone.Core.Providers
public TvdbSearchResult GetSeries(string title)
{
return SearchSeries(title)[0];
var searchResults = SearchSeries(title);
if (searchResults.Count == 0)
return null;
return searchResults[0];
}
public TvdbSeries GetSeries(int id, TvdbLanguage language)
public TvdbSeries GetSeries(int id, bool loadEpisodes)
{
Logger.Debug("Fetching seriesId'{0}' - '{1}' from tvdb", id, language);
return _handler.GetSeries(id, language, true, false, false);
Logger.Debug("Fetching SeriesId'{0}' from tvdb", id);
return _handler.GetSeries(id, TvdbLanguage.DefaultLanguage, loadEpisodes, false, false);
}
/// <summary>

View File

@ -2,6 +2,7 @@
namespace NzbDrone.Core.Repository
{
[SubSonicTableNameOverride("Config")]
public class Config
{
[SubSonicPrimaryKey]

View File

@ -1,27 +0,0 @@
using System;
using NzbDrone.Core.Repository.Quality;
using SubSonic.SqlGeneration.Schema;
namespace NzbDrone.Core.Repository
{
public class Episode
{
[SubSonicPrimaryKey(false)]
public long EpisodeId { get; set; }
public long SeriesId { get; set; }
public string Title { get; set; }
public long SeasonId { get; set; }
public int SeasonNumber { get; set; }
public int EpisodeNumber { get; set; }
public DateTime AirDate { get; set; }
public QualityTypes Quality { get; set; }
public bool Proper { get; set; }
[SubSonicToOneRelation(ThisClassContainsJoinKey = true)]
public virtual Season Season { get; private set; }
[SubSonicToOneRelation(ThisClassContainsJoinKey = true)]
public virtual Series Series { get; private set; }
}
}

View File

@ -0,0 +1,16 @@
using System;
using NzbDrone.Core.Repository.Quality;
using SubSonic.SqlGeneration.Schema;
namespace NzbDrone.Core.Repository.Episode
{
public class Episode
{
public virtual int SeriesId { get; set; }
public int SeasonNumber { get; set; }
public int EpisodeNumber { get; set; }
[SubSonicToOneRelation(ThisClassContainsJoinKey = true)]
public virtual Series Series { get; private set; }
}
}

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SubSonic.SqlGeneration.Schema;
namespace NzbDrone.Core.Repository.Episode
{
[SubSonicTableNameOverride("EpisodeInfo")]
public class EpisodeInfo : Episode
{
[SubSonicPrimaryKey(false)]
public virtual int EpisodeId { get; set; }
public int SeasonId { get; set; }
public string Title { get; set; }
public DateTime AirDate { get; set; }
[SubSonicLongString]
public string Overview { get; set; }
public string Language { get; set; }
[SubSonicToOneRelation(ThisClassContainsJoinKey = true)]
public virtual Season Season { get; set; }
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel.Syndication;
using System.Text;
using NzbDrone.Core.Repository.Quality;
using SubSonic.SqlGeneration.Schema;
namespace NzbDrone.Core.Repository.Episode
{
public class RemoteEpisode : Episode
{
public QualityTypes Quality { get; set; }
public SyndicationItem Feed { get; set; }
public bool Proper { get; set; }
}
}

View File

@ -1,11 +0,0 @@
using System;
using System.ServiceModel.Syndication;
using SubSonic.SqlGeneration.Schema;
namespace NzbDrone.Core.Repository
{
public class LocalEpisode : Episode
{
public String Path { get; set; }
}
}

View File

@ -1,12 +0,0 @@
using System;
using System.ServiceModel.Syndication;
using SubSonic.SqlGeneration.Schema;
namespace NzbDrone.Core.Repository
{
public class RemoteEpisode : Episode
{
[SubSonicIgnore]
public SyndicationItem Feed { get; set; }
}
}

View File

@ -8,16 +8,16 @@ namespace NzbDrone.Core.Repository
public class Season
{
[SubSonicPrimaryKey(false)]
public long SeasonId { get; set; }
public virtual long SeasonId { get; set; }
public long SeriesId { get; set; }
public int SeasonNumber { get; set; }
public bool Monitored { get; set; }
public string Folder { get; set; }
[SubSonicToManyRelation]
public virtual List<Episode> Episodes { get; set; }
public virtual List<Episode.Episode> Episodes { get; private set; }
[SubSonicToOneRelation(ThisClassContainsJoinKey = true)]
public virtual Series Series { get; set; }
public virtual Series Series { get; private set; }
}
}

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using NzbDrone.Core.Repository.Episode;
using SubSonic.SqlGeneration.Schema;
namespace NzbDrone.Core.Repository
@ -7,7 +8,7 @@ namespace NzbDrone.Core.Repository
public class Series
{
[SubSonicPrimaryKey(false)]
public int TvdbId { get; set; }
public virtual int SeriesId { get; set; }
public string Title { get; set; }
@ -32,7 +33,7 @@ namespace NzbDrone.Core.Repository
public virtual List<Season> Seasons { get; private set; }
[SubSonicToManyRelation]
public virtual List<Episode> Episodes { get; private set; }
public virtual List<EpisodeInfo> Episodes { get; private set; }
}

View File

@ -10,12 +10,14 @@ namespace NzbDrone.Web.Controllers
public class SeriesController : Controller
{
private readonly ISeriesProvider _seriesProvider;
private readonly IEpisodeProvider _episodeProvider;
//
// GET: /Series/
public SeriesController(ISeriesProvider seriesProvider)
public SeriesController(ISeriesProvider seriesProvider, IEpisodeProvider episodeProvider)
{
_seriesProvider = seriesProvider;
_episodeProvider = episodeProvider;
}
public ActionResult Index()
@ -39,90 +41,23 @@ namespace NzbDrone.Web.Controllers
}
public ActionResult LoadEpisodes(int seriesId)
{
_episodeProvider.RefreshSeries(seriesId);
return RedirectToAction("Details", new
{
seriesId = seriesId
});
}
//
// GET: /Series/Details/5
public ActionResult Details(int tvdbId)
public ActionResult Details(int seriesId)
{
return View(_seriesProvider.GetSeries(tvdbId));
return View(_seriesProvider.GetSeries(seriesId));
}
//
// GET: /Series/Create
public ActionResult Create()
{
return View();
}
//
// POST: /Series/Create
[HttpPost]
public ActionResult Create(FormCollection collection)
{
try
{
// TODO: Add insert logic here
return RedirectToAction("Index");
}
catch
{
return View();
}
}
//
// GET: /Series/Edit/5
public ActionResult Edit(int id)
{
return View();
}
//
// POST: /Series/Edit/5
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
try
{
// TODO: Add update logic here
return RedirectToAction("Index");
}
catch
{
return View();
}
}
//
// GET: /Series/Delete/5
public ActionResult Delete(int id)
{
return View();
}
//
// POST: /Series/Delete/5
[HttpPost]
public ActionResult Delete(int id, FormCollection collection)
{
try
{
// TODO: Add delete logic here
return RedirectToAction("Index");
}
catch
{
return View();
}
}
}
}

View File

@ -27,7 +27,7 @@ namespace NzbDrone.Web
protected override void OnApplicationStarted()
{
Main.ConfigureNlog();
CentralDispatch.ConfigureNlog();
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
base.OnApplicationStarted();
@ -36,7 +36,7 @@ namespace NzbDrone.Web
protected override IKernel CreateKernel()
{
_kernel = new StandardKernel();
Main.BindKernel(_kernel);
CentralDispatch.BindKernel(_kernel);
return _kernel;
}

View File

@ -1,35 +1,52 @@
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<NzbDrone.Core.Repository.Series>" %>
<%@ Import Namespace="Telerik.Web.Mvc.UI" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
<%: Model.Title %>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<fieldset>
<div class="display-label">ID</div>
<div class="display-field"><%: Model.TvdbId %></div>
<div class="display-label">Overview</div>
<div class="display-field"><%: Model.Overview %></div>
<div class="display-label">Status</div>
<div class="display-field"><%: Model.Status %></div>
<div class="display-label">AirTimes</div>
<div class="display-field"><%: Model.AirTimes %></div>
<div class="display-label">Language</div>
<div class="display-field"><%: Model.Language.ToUpper() %></div>
<div class="display-label">Location</div>
<div class="display-field"><%: Model.Path %></div>
<div class="display-label">
ID</div>
<div class="display-field">
<%: Model.SeriesId %></div>
<div class="display-label">
Overview</div>
<div class="display-field">
<%: Model.Overview %></div>
<div class="display-label">
Status</div>
<div class="display-field">
<%: Model.Status %></div>
<div class="display-label">
AirTimes</div>
<div class="display-field">
<%: Model.AirTimes %></div>
<div class="display-label">
Language</div>
<div class="display-field">
<%: Model.Language.ToUpper() %></div>
<div class="display-label">
Location</div>
<div class="display-field">
<%: Model.Path %></div>
</fieldset>
<%= Html.Telerik().Grid(Model.Episodes)
.Name("Episodes")
.Columns(columns =>
{
columns.Bound(c => c.EpisodeNumber).Width(10);
columns.Bound(c => c.Title);
columns.Bound(c => c.AirDate).Format("{0:d}").Width(150);
})
.Groupable(grouping => grouping.Groups(groups => groups.Add(c => c.SeasonNumber)))
.Sortable(rows=>rows
.OrderBy(epSort => epSort.Add(c => c.EpisodeNumber)))
%>
<p>
<%-- <%: Html.ActionLink("Edit", "Edit", new { /* id=Model.PrimaryKey */ }) %> |--%>
<%: Html.ActionLink("Back to List", "Index") %>
<%: Html.ActionLink("Load Episodes", "LoadEpisodes", new{seriesId= Model.SeriesId}) %>
</p>
</asp:Content>

View File

@ -20,11 +20,11 @@
.Name("Grid")
.Columns(columns =>
{
columns.Bound(o => o.TvdbId).Width(100);
columns.Bound(o => o.SeriesId).Width(100);
columns.Template(c =>
{
%>
<%:Html.ActionLink(c.Title, "Details", new {tvdbId =c.TvdbId}) %>
<%:Html.ActionLink(c.Title, "Details", new {seriesId =c.SeriesId}) %>
<%
}).Title("Title");
columns.Bound(o => o.Status);