Merge branch 'markus101'

pull/4/head
kay.one 13 years ago
commit eaaf464d38

@ -55,10 +55,12 @@ namespace NzbDrone.Core.Test
mocker.GetMock<EpisodeProvider>() mocker.GetMock<EpisodeProvider>()
.Setup(c => c.MarkEpisodeAsFetched(12)); .Setup(c => c.MarkEpisodeAsFetched(12));
mocker.GetMock<EpisodeProvider>() mocker.GetMock<EpisodeProvider>()
.Setup(c => c.MarkEpisodeAsFetched(99)); .Setup(c => c.MarkEpisodeAsFetched(99));
mocker.GetMock<ExternalNotificationProvider>()
.Setup(c => c.OnGrab(It.IsAny<string>()));
mocker.Resolve<DownloadProvider>().DownloadReport(parseResult); mocker.Resolve<DownloadProvider>().DownloadReport(parseResult);
mocker.VerifyAllMocks(); mocker.VerifyAllMocks();

@ -991,5 +991,246 @@ namespace NzbDrone.Core.Test
mocker.VerifyAllMocks(); mocker.VerifyAllMocks();
} }
[Test]
public void IgnoreEpisode_Ignore()
{
var db = MockLib.GetEmptyDatabase();
var mocker = new AutoMoqer();
mocker.SetConstant(db);
var episodes = Builder<Episode>.CreateListOfSize(4)
.WhereAll()
.Have(c => c.SeriesId = 10)
.Have(c => c.SeasonNumber = 1)
.Have(c => c.Ignored = false)
.Build().ToList();
episodes.ForEach(c => db.Insert(c));
//Act
mocker.Resolve<EpisodeProvider>().SetEpisodeIgnore(1, true);
//Assert
var episodesInDb = db.Fetch<Episode>(@"SELECT * FROM Episodes");
episodesInDb.Should().HaveCount(4);
episodesInDb.Where(e => e.Ignored).Should().HaveCount(1);
mocker.VerifyAllMocks();
}
[Test]
public void IgnoreEpisode_RemoveIgnore()
{
var db = MockLib.GetEmptyDatabase();
var mocker = new AutoMoqer();
mocker.SetConstant(db);
var episodes = Builder<Episode>.CreateListOfSize(4)
.WhereAll()
.Have(c => c.SeriesId = 10)
.Have(c => c.SeasonNumber = 1)
.Have(c => c.Ignored = true)
.Build().ToList();
episodes.ForEach(c => db.Insert(c));
//Act
mocker.Resolve<EpisodeProvider>().SetEpisodeIgnore(1, false);
//Assert
var episodesInDb = db.Fetch<Episode>(@"SELECT * FROM Episodes");
episodesInDb.Should().HaveCount(4);
episodesInDb.Where(e => !e.Ignored).Should().HaveCount(1);
mocker.VerifyAllMocks();
}
[Test]
public void IgnoreSeason_Ignore()
{
var db = MockLib.GetEmptyDatabase();
var mocker = new AutoMoqer();
mocker.SetConstant(db);
var episodes = Builder<Episode>.CreateListOfSize(4)
.WhereAll()
.Have(c => c.SeriesId = 10)
.Have(c => c.SeasonNumber = 1)
.Have(c => c.Ignored = false)
.Build().ToList();
episodes.ForEach(c => db.Insert(c));
//Act
mocker.Resolve<EpisodeProvider>().SetSeasonIgnore(10, 1, true);
//Assert
var episodesInDb = db.Fetch<Episode>(@"SELECT * FROM Episodes");
episodesInDb.Should().HaveCount(4);
episodesInDb.Where(e => e.Ignored).Should().HaveCount(4);
mocker.VerifyAllMocks();
}
[Test]
public void IgnoreSeason_RemoveIgnore()
{
var db = MockLib.GetEmptyDatabase();
var mocker = new AutoMoqer();
mocker.SetConstant(db);
var episodes = Builder<Episode>.CreateListOfSize(4)
.WhereAll()
.Have(c => c.SeriesId = 10)
.Have(c => c.SeasonNumber = 1)
.Have(c => c.Ignored = true)
.Build().ToList();
episodes.ForEach(c => db.Insert(c));
//Act
mocker.Resolve<EpisodeProvider>().SetSeasonIgnore(10, 1, false);
//Assert
var episodesInDb = db.Fetch<Episode>(@"SELECT * FROM Episodes");
episodesInDb.Should().HaveCount(4);
episodesInDb.Where(e => !e.Ignored).Should().HaveCount(4);
mocker.VerifyAllMocks();
}
[Test]
public void IgnoreSeason_Ignore_Half()
{
var db = MockLib.GetEmptyDatabase();
var mocker = new AutoMoqer();
mocker.SetConstant(db);
var episodes = Builder<Episode>.CreateListOfSize(4)
.WhereAll()
.Have(c => c.SeriesId = 10)
.Have(c => c.SeasonNumber = 1)
.WhereTheFirst(2)
.Have(c => c.Ignored = false)
.AndTheRemaining()
.Have(c => c.Ignored = true)
.Build().ToList();
episodes.ForEach(c => db.Insert(c));
//Act
mocker.Resolve<EpisodeProvider>().SetSeasonIgnore(10, 1, true);
//Assert
var episodesInDb = db.Fetch<Episode>(@"SELECT * FROM Episodes");
episodesInDb.Should().HaveCount(4);
episodesInDb.Where(e => e.Ignored).Should().HaveCount(4);
mocker.VerifyAllMocks();
}
[Test]
public void EpisodesWithoutFiles_no_specials()
{
var db = MockLib.GetEmptyDatabase();
var mocker = new AutoMoqer();
mocker.SetConstant(db);
var series = Builder<Series>.CreateNew()
.With(s => s.SeriesId = 10)
.Build();
var episodes = Builder<Episode>.CreateListOfSize(4)
.WhereAll()
.Have(c => c.SeriesId = 10)
.Have(c => c.SeasonNumber = 1)
.Have(c => c.AirDate = DateTime.Today.AddDays(-4))
.Have(c => c.Ignored = true)
.WhereTheFirst(2)
.Have(c => c.EpisodeFileId = 0)
.WhereSection(1, 2)
.Have(c => c.Ignored = false)
.Build().ToList();
var specials = Builder<Episode>.CreateListOfSize(2)
.WhereAll()
.Have(c => c.SeriesId = 10)
.Have(c => c.SeasonNumber = 0)
.Have(c => c.AirDate = DateTime.Today.AddDays(-4))
.Have(c => c.EpisodeFileId = 0)
.WhereTheFirst(1)
.Have(c => c.Ignored = true)
.AndTheRemaining()
.Have(c => c.Ignored = false)
.Build().ToList();
db.Insert(series);
db.InsertMany(episodes);
db.InsertMany(specials);
//Act
var missingFiles= mocker.Resolve<EpisodeProvider>().EpisodesWithoutFiles(false);
//Assert
missingFiles.Should().HaveCount(1);
missingFiles.Where(e => e.EpisodeFileId == 0).Should().HaveCount(1);
mocker.VerifyAllMocks();
}
[Test]
public void EpisodesWithoutFiles_with_specials()
{
var db = MockLib.GetEmptyDatabase();
var mocker = new AutoMoqer();
mocker.SetConstant(db);
var series = Builder<Series>.CreateNew()
.With(s => s.SeriesId = 10)
.Build();
var episodes = Builder<Episode>.CreateListOfSize(4)
.WhereAll()
.Have(c => c.SeriesId = 10)
.Have(c => c.SeasonNumber = 1)
.Have(c => c.AirDate = DateTime.Today.AddDays(-4))
.Have(c => c.Ignored = true)
.WhereTheFirst(2)
.Have(c => c.EpisodeFileId = 0)
.WhereSection(1,2)
.Have(c => c.Ignored = false)
.Build().ToList();
var specials = Builder<Episode>.CreateListOfSize(2)
.WhereAll()
.Have(c => c.SeriesId = 10)
.Have(c => c.SeasonNumber = 0)
.Have(c => c.AirDate = DateTime.Today.AddDays(-4))
.Have(c => c.EpisodeFileId = 0)
.WhereTheFirst(1)
.Have(c => c.Ignored = true)
.AndTheRemaining()
.Have(c => c.Ignored = false)
.Build().ToList();
db.Insert(series);
db.InsertMany(episodes);
db.InsertMany(specials);
//Act
var missingFiles = mocker.Resolve<EpisodeProvider>().EpisodesWithoutFiles(true);
//Assert
missingFiles.Should().HaveCount(2);
missingFiles.Where(e => e.EpisodeFileId == 0).Should().HaveCount(2);
mocker.VerifyAllMocks();
}
} }
} }

@ -41,6 +41,7 @@ namespace NzbDrone.Core.Test
[TestCase("Adventure.Inc.S03E19.DVDRip.\"XviD\"-OSiTV", "Adventure.Inc", 3, 19)] [TestCase("Adventure.Inc.S03E19.DVDRip.\"XviD\"-OSiTV", "Adventure.Inc", 3, 19)]
[TestCase("Hawaii Five-0 (2010) - 1x05 - Nalowale (Forgotten/Missing)", "Hawaii Five-0 (2010)", 1, 5)] [TestCase("Hawaii Five-0 (2010) - 1x05 - Nalowale (Forgotten/Missing)", "Hawaii Five-0 (2010)", 1, 5)]
[TestCase("Hawaii Five-0 (2010) - 1x05 - Title", "Hawaii Five-0 (2010)", 1, 5)] [TestCase("Hawaii Five-0 (2010) - 1x05 - Title", "Hawaii Five-0 (2010)", 1, 5)]
[TestCase("House - S06E13 - 5 to 9 [DVD]", "House", 6, 13)]
public void ParseTitle_single(string postTitle, string title, int seasonNumber, int episodeNumber) public void ParseTitle_single(string postTitle, string title, int seasonNumber, int episodeNumber)
{ {
var result = Parser.ParseTitle(postTitle); var result = Parser.ParseTitle(postTitle);
@ -131,7 +132,7 @@ namespace NzbDrone.Core.Test
[TestCase("Two.and.a.Half.Men.103.104.720p.HDTV.X264-DIMENSION", "Two.and.a.Half.Men", 1, new[] { 3, 4 }, 2)] [TestCase("Two.and.a.Half.Men.103.104.720p.HDTV.X264-DIMENSION", "Two.and.a.Half.Men", 1, new[] { 3, 4 }, 2)]
[TestCase("Weeds.S03E01.S03E02.720p.HDTV.X264-DIMENSION", "Weeds", 3, new[] { 1, 2 }, 2)] [TestCase("Weeds.S03E01.S03E02.720p.HDTV.X264-DIMENSION", "Weeds", 3, new[] { 1, 2 }, 2)]
[TestCase("The Borgias S01e01 e02 ShoHD On Demand 1080i DD5 1 ALANiS", "The Borgias", 1, new[] { 1, 2 }, 2)] [TestCase("The Borgias S01e01 e02 ShoHD On Demand 1080i DD5 1 ALANiS", "The Borgias", 1, new[] { 1, 2 }, 2)]
[TestCase("Big Time Rush 1x01 to 10 480i DD2 0 Sianto", "Big Time Rush", 1, new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, 10)] //[TestCase("Big Time Rush 1x01 to 10 480i DD2 0 Sianto", "Big Time Rush", 1, new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, 10)]
[TestCase("White.Collar.2x04.2x05.720p.BluRay-FUTV", "White.Collar", 2, new[] { 4, 5 }, 2)] [TestCase("White.Collar.2x04.2x05.720p.BluRay-FUTV", "White.Collar", 2, new[] { 4, 5 }, 2)]
[TestCase("Desperate.Housewives.S07E22E23.720p.HDTV.X264-DIMENSION", "Desperate.Housewives", 7, new[] { 22, 23 }, 2)] [TestCase("Desperate.Housewives.S07E22E23.720p.HDTV.X264-DIMENSION", "Desperate.Housewives", 7, new[] { 22, 23 }, 2)]
[TestCase("S07E22 - 7x23 - And Lots of Security.. [HDTV].mkv", "", 7, new[] { 22, 23 }, 2)] [TestCase("S07E22 - 7x23 - And Lots of Security.. [HDTV].mkv", "", 7, new[] { 22, 23 }, 2)]
@ -203,6 +204,8 @@ namespace NzbDrone.Core.Test
[TestCase("peri.od", "period")] [TestCase("peri.od", "period")]
[TestCase("this.^&%^**$%@#$!That", "thisthat")] [TestCase("this.^&%^**$%@#$!That", "thisthat")]
[TestCase("test/test", "testtest")] [TestCase("test/test", "testtest")]
[TestCase("90210", "90210")]
[TestCase("24", "24")]
public void Normalize_Title(string dirty, string clean) public void Normalize_Title(string dirty, string clean)
{ {
var result = Parser.NormalizeTitle(dirty); var result = Parser.NormalizeTitle(dirty);

@ -10,6 +10,7 @@ using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Repository; using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality; using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using PetaPoco;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace NzbDrone.Core.Test namespace NzbDrone.Core.Test
@ -350,5 +351,129 @@ namespace NzbDrone.Core.Test
series.QualityProfile.Should().NotBeNull(); series.QualityProfile.Should().NotBeNull();
series.QualityProfileId.Should().Be(fakeQuality.QualityProfileId); series.QualityProfileId.Should().Be(fakeQuality.QualityProfileId);
} }
[Test]
public void SeriesPathExists_exact_match()
{
var mocker = new AutoMoqer(MockBehavior.Strict);
var db = MockLib.GetEmptyDatabase();
mocker.SetConstant(db);
var path = @"C:\Test\TV\30 Rock";
var fakeSeries = Builder<Series>.CreateListOfSize(10)
.WhereAll()
.Have(c => c.QualityProfileId = 1)
.WhereTheFirst(1)
.Have(c => c.Path = path)
.Build();
var fakeQuality = Builder<QualityProfile>.CreateNew().Build();
db.InsertMany(fakeSeries);
db.Insert(fakeQuality);
//Act
mocker.Resolve<QualityProvider>();
//mocker.GetMock<IDatabase>().Setup(s => s.Fetch<Series, QualityProfile>(It.IsAny<string>())).Returns(
//fakeSeries.ToList());
var result = mocker.Resolve<SeriesProvider>().SeriesPathExists(path);
//Assert
result.Should().BeTrue();
}
[Test]
public void SeriesPathExists_match()
{
var mocker = new AutoMoqer(MockBehavior.Strict);
var db = MockLib.GetEmptyDatabase();
mocker.SetConstant(db);
var path = @"C:\Test\TV\30 Rock";
var fakeSeries = Builder<Series>.CreateListOfSize(10)
.WhereAll()
.Have(c => c.QualityProfileId = 1)
.WhereTheFirst(1)
.Have(c => c.Path = path)
.Build();
var fakeQuality = Builder<QualityProfile>.CreateNew().Build();
db.InsertMany(fakeSeries);
db.Insert(fakeQuality);
//Act
mocker.Resolve<QualityProvider>();
//mocker.GetMock<IDatabase>().Setup(s => s.Fetch<Series, QualityProfile>(It.IsAny<string>())).Returns(
//fakeSeries.ToList());
var result = mocker.Resolve<SeriesProvider>().SeriesPathExists(path.ToUpper());
//Assert
result.Should().BeTrue();
}
[Test]
public void SeriesPathExists_match_alt()
{
var mocker = new AutoMoqer(MockBehavior.Strict);
var db = MockLib.GetEmptyDatabase();
mocker.SetConstant(db);
var path = @"C:\Test\TV\The Simpsons";
var fakeSeries = Builder<Series>.CreateListOfSize(10)
.WhereAll()
.Have(c => c.QualityProfileId = 1)
.WhereTheFirst(1)
.Have(c => c.Path = path)
.Build();
var fakeQuality = Builder<QualityProfile>.CreateNew().Build();
db.InsertMany(fakeSeries);
db.Insert(fakeQuality);
//Act
mocker.Resolve<QualityProvider>();
//mocker.GetMock<IDatabase>().Setup(s => s.Fetch<Series, QualityProfile>(It.IsAny<string>())).Returns(
//fakeSeries.ToList());
var result = mocker.Resolve<SeriesProvider>().SeriesPathExists(@"c:\Test\Tv\the sIMpsons");
//Assert
result.Should().BeTrue();
}
[Test]
public void SeriesPathExists_match_false()
{
var mocker = new AutoMoqer(MockBehavior.Strict);
var db = MockLib.GetEmptyDatabase();
mocker.SetConstant(db);
var path = @"C:\Test\TV\30 Rock";
var fakeSeries = Builder<Series>.CreateListOfSize(10)
.WhereAll()
.Have(c => c.QualityProfileId = 1)
.WhereTheFirst(1)
.Have(c => c.Path = path)
.Build();
var fakeQuality = Builder<QualityProfile>.CreateNew().Build();
db.InsertMany(fakeSeries);
db.Insert(fakeQuality);
//Act
mocker.Resolve<QualityProvider>();
//mocker.GetMock<IDatabase>().Setup(s => s.Fetch<Series, QualityProfile>(It.IsAny<string>())).Returns(
//fakeSeries.ToList());
var result = mocker.Resolve<SeriesProvider>().SeriesPathExists(@"C:\Test\TV\Not A match");
//Assert
result.Should().BeFalse();
}
} }
} }

@ -62,9 +62,9 @@ namespace NzbDrone.Core
_kernel.Get<QualityProvider>().SetupDefaultProfiles(); _kernel.Get<QualityProvider>().SetupDefaultProfiles();
BindExternalNotifications();
BindIndexers(); BindIndexers();
BindJobs(); BindJobs();
BindExternalNotifications();
} }
private static void BindKernel() private static void BindKernel()
@ -111,8 +111,9 @@ namespace NzbDrone.Core
private static void BindExternalNotifications() private static void BindExternalNotifications()
{ {
_kernel.Bind<ExternalNotificationProviderBase>().To<XbmcNotificationProvider>().InSingletonScope(); _kernel.Bind<ExternalNotificationBase>().To<Xbmc>();
var notifiers = _kernel.GetAll<ExternalNotificationProviderBase>();
var notifiers = _kernel.GetAll<ExternalNotificationBase>();
_kernel.Get<ExternalNotificationProvider>().InitializeNotifiers(notifiers.ToList()); _kernel.Get<ExternalNotificationProvider>().InitializeNotifiers(notifiers.ToList());
} }

@ -0,0 +1,30 @@
using System;
using System.Data;
using Migrator.Framework;
namespace NzbDrone.Core.Datastore.Migrations
{
[Migration(20110726)]
public class Migration20110726 : Migration
{
public override void Up()
{
Database.RemoveTable("ExternalNotificationSettings");
Database.AddTable("ExternalNotificationDefinitions", new[]
{
new Column("Id", DbType.Int32, ColumnProperty.PrimaryKeyWithIdentity),
new Column("Enable", DbType.Boolean, ColumnProperty.NotNull),
new Column("ExternalNotificationProviderType", DbType.String, ColumnProperty.NotNull),
new Column("Name", DbType.String, ColumnProperty.NotNull)
});
}
public override void Down()
{
throw new NotImplementedException();
}
}
}

@ -6,7 +6,7 @@ namespace NzbDrone.Core.Model.Notification
{ {
public BasicNotification() public BasicNotification()
{ {
Id = Guid.Empty; Id = Guid.NewGuid();
} }
/// <summary> /// <summary>

@ -176,6 +176,7 @@
<Compile Include="Datastore\MigrationLogger.cs" /> <Compile Include="Datastore\MigrationLogger.cs" />
<Compile Include="Datastore\MigrationsHelper.cs" /> <Compile Include="Datastore\MigrationsHelper.cs" />
<Compile Include="Datastore\CustomeMapper.cs" /> <Compile Include="Datastore\CustomeMapper.cs" />
<Compile Include="Datastore\Migrations\Migration20110726.cs" />
<Compile Include="Datastore\Migrations\Migration20110707.cs" /> <Compile Include="Datastore\Migrations\Migration20110707.cs" />
<Compile Include="Fluent.cs" /> <Compile Include="Fluent.cs" />
<Compile Include="Helpers\EpisodeSortingHelper.cs" /> <Compile Include="Helpers\EpisodeSortingHelper.cs" />
@ -203,8 +204,8 @@
<Compile Include="Model\Xbmc\VersionResult.cs" /> <Compile Include="Model\Xbmc\VersionResult.cs" />
<Compile Include="Providers\DiskScanProvider.cs" /> <Compile Include="Providers\DiskScanProvider.cs" />
<Compile Include="Providers\DownloadProvider.cs" /> <Compile Include="Providers\DownloadProvider.cs" />
<Compile Include="Providers\ExternalNotification\ExternalNotificationProviderBase.cs" /> <Compile Include="Providers\ExternalNotification\ExternalNotificationBase.cs" />
<Compile Include="Providers\ExternalNotification\XbmcNotificationProvider.cs" /> <Compile Include="Providers\ExternalNotification\Xbmc.cs" />
<Compile Include="Providers\InventoryProvider.cs" /> <Compile Include="Providers\InventoryProvider.cs" />
<Compile Include="Providers\Indexer\SyndicationFeedXmlReader.cs" /> <Compile Include="Providers\Indexer\SyndicationFeedXmlReader.cs" />
<Compile Include="Providers\AutoConfigureProvider.cs" /> <Compile Include="Providers\AutoConfigureProvider.cs" />
@ -224,7 +225,7 @@
<Compile Include="Providers\Jobs\UpdateInfoJob.cs" /> <Compile Include="Providers\Jobs\UpdateInfoJob.cs" />
<Compile Include="Providers\SceneMappingProvider.cs" /> <Compile Include="Providers\SceneMappingProvider.cs" />
<Compile Include="Providers\Xbmc\EventClientProvider.cs" /> <Compile Include="Providers\Xbmc\EventClientProvider.cs" />
<Compile Include="Repository\ExternalNotificationSetting.cs" /> <Compile Include="Repository\ExternalNotificationDefinition.cs" />
<Compile Include="Repository\JobDefinition.cs" /> <Compile Include="Repository\JobDefinition.cs" />
<Compile Include="Repository\IndexerDefinition.cs" /> <Compile Include="Repository\IndexerDefinition.cs" />
<Compile Include="Model\EpisodeParseResult.cs" /> <Compile Include="Model\EpisodeParseResult.cs" />

@ -19,11 +19,11 @@ namespace NzbDrone.Core
RegexOptions.IgnoreCase | RegexOptions.Compiled), RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Multi-Part episodes without a title (S01E05.S01E06) //Multi-Part episodes without a title (S01E05.S01E06)
new Regex(@"^(?:\W*S?(?<season>\d{1,2}(?!\d+))(?:(?:\-|\.|[ex]|\s|\sto\s){1,2}(?<episode>\d{1,2}(?!\d+)))+){2,}\W?(?!\\)", new Regex(@"^(?:\W*S?(?<season>\d{1,2}(?!\d+))(?:(?:\-|\.|[ex]|\s){1,2}(?<episode>\d{1,2}(?!\d+)))+){2,}\W?(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled), RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Single episodes or multi-episode (S01E05E06, S01E05-06, etc) //Single episodes or multi-episode (S01E05E06, S01E05-06, etc)
new Regex(@"^(?<title>.+?)(?:\W+S?(?<season>\d{1,2}(?!\d+))(?:(?:\-|\.|[ex]|\s|\sto\s){1,2}(?<episode>\d{1,2}(?!\d+)))+)+\W?(?!\\)", new Regex(@"^(?<title>.+?)(?:\W+S?(?<season>\d{1,2}(?!\d+))(?:(?:\-|\.|[ex]|\s){1,2}(?<episode>\d{1,2}(?!\d+)))+)+\W?(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled), RegexOptions.IgnoreCase | RegexOptions.Compiled),
//No Title - Single episodes or multi-episode (S01E05E06, S01E05-06, etc) //No Title - Single episodes or multi-episode (S01E05E06, S01E05-06, etc)
@ -381,6 +381,9 @@ namespace NzbDrone.Core
/// <returns></returns> /// <returns></returns>
public static string NormalizeTitle(string title) public static string NormalizeTitle(string title)
{ {
//Todo: Find a better way to do this hack
if (title == "90210" || title == "24")
return title;
return NormalizeRegex.Replace(title, String.Empty).ToLower(); return NormalizeRegex.Replace(title, String.Empty).ToLower();
} }

@ -18,18 +18,22 @@ namespace NzbDrone.Core.Providers
private readonly EpisodeProvider _episodeProvider; private readonly EpisodeProvider _episodeProvider;
private readonly MediaFileProvider _mediaFileProvider; private readonly MediaFileProvider _mediaFileProvider;
private readonly SeriesProvider _seriesProvider; private readonly SeriesProvider _seriesProvider;
private readonly ExternalNotificationProvider _externalNotificationProvider;
private readonly SabProvider _sabProvider;
[Inject] [Inject]
public DiskScanProvider(DiskProvider diskProvider, EpisodeProvider episodeProvider, public DiskScanProvider(DiskProvider diskProvider, EpisodeProvider episodeProvider,
SeriesProvider seriesProvider, MediaFileProvider mediaFileProvider) SeriesProvider seriesProvider, MediaFileProvider mediaFileProvider,
ExternalNotificationProvider externalNotificationProvider, SabProvider sabProvider)
{ {
_diskProvider = diskProvider; _diskProvider = diskProvider;
_episodeProvider = episodeProvider; _episodeProvider = episodeProvider;
_seriesProvider = seriesProvider; _seriesProvider = seriesProvider;
_mediaFileProvider = mediaFileProvider; _mediaFileProvider = mediaFileProvider;
_externalNotificationProvider = externalNotificationProvider;
_sabProvider = sabProvider;
} }
public DiskScanProvider() public DiskScanProvider()
{ {
} }
@ -142,7 +146,7 @@ namespace NzbDrone.Core.Providers
return episodeFile; return episodeFile;
} }
public virtual bool MoveEpisodeFile(EpisodeFile episodeFile) public virtual bool MoveEpisodeFile(EpisodeFile episodeFile, bool newDownload = false)
{ {
if (episodeFile == null) if (episodeFile == null)
throw new ArgumentNullException("episodeFile"); throw new ArgumentNullException("episodeFile");
@ -163,6 +167,18 @@ namespace NzbDrone.Core.Providers
episodeFile.Path = newFile.FullName; episodeFile.Path = newFile.FullName;
_mediaFileProvider.Update(episodeFile); _mediaFileProvider.Update(episodeFile);
//ExternalNotification
var parseResult = Parser.ParsePath(episodeFile.Path);
parseResult.Series = series;
var message = _sabProvider.GetSabTitle(parseResult);
if (newDownload)
_externalNotificationProvider.OnDownload(message, series);
else
_externalNotificationProvider.OnRename(message, series);
return true; return true;
} }

@ -11,14 +11,18 @@ namespace NzbDrone.Core.Providers
private readonly SabProvider _sabProvider; private readonly SabProvider _sabProvider;
private readonly HistoryProvider _historyProvider; private readonly HistoryProvider _historyProvider;
private readonly EpisodeProvider _episodeProvider; private readonly EpisodeProvider _episodeProvider;
private readonly ExternalNotificationProvider _externalNotificationProvider;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
[Inject] [Inject]
public DownloadProvider(SabProvider sabProvider, HistoryProvider historyProvider, EpisodeProvider episodeProvider) public DownloadProvider(SabProvider sabProvider, HistoryProvider historyProvider,
EpisodeProvider episodeProvider, ExternalNotificationProvider externalNotificationProvider)
{ {
_sabProvider = sabProvider; _sabProvider = sabProvider;
_historyProvider = historyProvider; _historyProvider = historyProvider;
_episodeProvider = episodeProvider; _episodeProvider = episodeProvider;
_externalNotificationProvider = externalNotificationProvider;
} }
public DownloadProvider() public DownloadProvider()
@ -55,6 +59,8 @@ namespace NzbDrone.Core.Providers
} }
} }
_externalNotificationProvider.OnGrab(sabTitle);
return addSuccess; return addSuccess;
} }
} }

@ -159,12 +159,15 @@ namespace NzbDrone.Core.Providers
public virtual IList<Episode> EpisodesWithoutFiles(bool includeSpecials) public virtual IList<Episode> EpisodesWithoutFiles(bool includeSpecials)
{ {
var episodes = _database.Query<Episode>("WHERE (EpisodeFileId=0 OR EpisodeFileId=NULL) AND AirDate<=@0", var episodes = _database.Query<Episode, Series>(@"SELECT Episodes.*, Series.Title FROM Episodes
INNER JOIN Series
ON Episodes.SeriesId = Series.SeriesId
WHERE (EpisodeFileId=0 OR EpisodeFileId=NULL) AND Ignored = 0 AND AirDate<=@0",
DateTime.Now.Date); DateTime.Now.Date);
if (includeSpecials) if (!includeSpecials)
return episodes.Where(e => e.SeasonNumber > 0).ToList(); return episodes.Where(e => e.SeasonNumber > 0).ToList();
return AttachSeries(episodes.ToList()); return episodes.ToList();
} }
public virtual IList<Episode> GetEpisodesByFileId(int episodeFileId) public virtual IList<Episode> GetEpisodesByFileId(int episodeFileId)
@ -296,26 +299,23 @@ namespace NzbDrone.Core.Providers
public virtual void SetSeasonIgnore(long seriesId, int seasonNumber, bool isIgnored) public virtual void SetSeasonIgnore(long seriesId, int seasonNumber, bool isIgnored)
{ {
Logger.Info("Setting ignore flag on Series:{0} Season:{1} to {2}", seriesId, seasonNumber, isIgnored); Logger.Info("Setting ignore flag on Series:{0} Season:{1} to {2}", seriesId, seasonNumber, isIgnored);
var episodes = GetEpisodesBySeason(seriesId, seasonNumber);
using (var tran = _database.GetTransaction()) _database.Execute(@"UPDATE Episodes SET Ignored = @0
{ WHERE SeriesId = @1 AND SeasonNumber = @2 AND Ignored = @3",
foreach (var episode in episodes) isIgnored, seriesId, seasonNumber, !isIgnored);
{
episode.Ignored = isIgnored;
_database.Update(episode);
}
//Shouldn't run if Database is a mock since transaction will be null
if (_database.GetType().Namespace != "Castle.Proxies" && tran != null)
{
tran.Complete();
}
Logger.Info("Ignore flag for Series:{0} Season:{1} successfully set to {2}", seriesId, seasonNumber, isIgnored); Logger.Info("Ignore flag for Series:{0} Season:{1} successfully set to {2}", seriesId, seasonNumber, isIgnored);
} }
public virtual void SetEpisodeIgnore(int episodeId, bool isIgnored)
{
Logger.Info("Setting ignore flag on Episode:{0} to {1}", episodeId, isIgnored);
_database.Execute(@"UPDATE Episodes SET Ignored = @0
WHERE EpisodeId = @1",
isIgnored, episodeId);
Logger.Info("Ignore flag for Episode:{0} successfully set to {1}", episodeId, isIgnored);
} }
public IList<Episode> AttachSeries(IList<Episode> episodes) public IList<Episode> AttachSeries(IList<Episode> episodes)

@ -6,16 +6,14 @@ using NzbDrone.Core.Repository;
namespace NzbDrone.Core.Providers.ExternalNotification namespace NzbDrone.Core.Providers.ExternalNotification
{ {
public abstract class ExternalNotificationProviderBase public abstract class ExternalNotificationBase
{ {
protected readonly Logger _logger; protected readonly Logger _logger;
protected readonly ConfigProvider _configProvider; protected readonly ConfigProvider _configProvider;
protected readonly ExternalNotificationProvider _externalNotificationProvider;
protected ExternalNotificationProviderBase(ConfigProvider configProvider, ExternalNotificationProvider externalNotificationProvider) protected ExternalNotificationBase(ConfigProvider configProvider)
{ {
_configProvider = configProvider; _configProvider = configProvider;
_externalNotificationProvider = externalNotificationProvider;
_logger = LogManager.GetLogger(GetType().ToString()); _logger = LogManager.GetLogger(GetType().ToString());
} }
@ -24,35 +22,6 @@ namespace NzbDrone.Core.Providers.ExternalNotification
/// </summary> /// </summary>
public abstract string Name { get; } public abstract string Name { get; }
public ExternalNotificationSetting Settings
{
get
{
return _externalNotificationProvider.GetSettings(GetType());
}
}
public virtual void Notify(ExternalNotificationType type, string message, int seriesId = 0)
{
if (type == ExternalNotificationType.Grab)
OnGrab(message);
else if (type == ExternalNotificationType.Download)
{
throw new NotImplementedException();
var series = new Series();
OnDownload(message, series);
}
else if (type == ExternalNotificationType.Rename)
{
throw new NotImplementedException();
var series = new Series();
OnRename(message, series);
}
}
/// <summary> /// <summary>
/// Performs the on grab action /// Performs the on grab action
/// </summary> /// </summary>

@ -0,0 +1,66 @@
using System;
using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Repository;
namespace NzbDrone.Core.Providers.ExternalNotification
{
public class Xbmc : ExternalNotificationBase
{
private readonly XbmcProvider _xbmcProvider;
public Xbmc(ConfigProvider configProvider, XbmcProvider xbmcProvider)
: base(configProvider)
{
_xbmcProvider = xbmcProvider;
}
public override string Name
{
get { return "XBMC"; }
}
public override void OnGrab(string message)
{
const string header = "NzbDrone [TV] - Grabbed";
if (_configProvider.XbmcNotifyOnGrab)
{
_logger.Trace("Sending Notification to XBMC");
_xbmcProvider.Notify(header, message);
}
}
public override void OnDownload(string message, Series series)
{
const string header = "NzbDrone [TV] - Downloaded";
if (_configProvider.XbmcNotifyOnDownload)
{
_logger.Trace("Sending Notification to XBMC");
_xbmcProvider.Notify(header, message);
}
UpdateAndClean(series);
}
public override void OnRename(string message, Series series)
{
UpdateAndClean(series);
}
private void UpdateAndClean(Series series)
{
if (_configProvider.XbmcUpdateLibrary)
{
_logger.Trace("Sending Update Request to XBMC");
_xbmcProvider.Update(series);
}
if (_configProvider.XbmcCleanLibrary)
{
_logger.Trace("Sending Clean DB Request to XBMC");
_xbmcProvider.Clean();
}
}
}
}

@ -1,92 +0,0 @@
using System;
using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Repository;
namespace NzbDrone.Core.Providers.ExternalNotification
{
public class XbmcNotificationProvider : ExternalNotificationProviderBase
{
private readonly XbmcProvider _xbmcProvider;
public XbmcNotificationProvider(ConfigProvider configProvider, XbmcProvider xbmcProvider,
ExternalNotificationProvider externalNotificationProvider)
: base(configProvider, externalNotificationProvider)
{
_xbmcProvider = xbmcProvider;
}
public override string Name
{
get { return "XBMC"; }
}
public override void OnGrab(string message)
{
const string header = "NzbDrone [TV] - Grabbed";
if (Convert.ToBoolean(_configProvider.GetValue("XbmcEnabled", false)))
{
if (Convert.ToBoolean(_configProvider.GetValue("XbmcNotifyOnGrab", false)))
{
_logger.Trace("Sending Notification to XBMC");
_xbmcProvider.Notify(header, message);
return;
}
_logger.Trace("XBMC NotifyOnGrab is not enabled");
}
_logger.Trace("XBMC Notifier is not enabled");
}
public override void OnDownload(string message, Series series)
{
const string header = "NzbDrone [TV] - Downloaded";
if (Convert.ToBoolean(_configProvider.GetValue("XbmcEnabled", false)))
{
if (Convert.ToBoolean(_configProvider.GetValue("XbmcNotifyOnDownload", false)))
{
_logger.Trace("Sending Notification to XBMC");
_xbmcProvider.Notify(header, message);
}
if (Convert.ToBoolean(_configProvider.GetValue("XbmcUpdateOnDownload", false)))
{
_logger.Trace("Sending Update Request to XBMC");
_xbmcProvider.Update(series);
}
if (Convert.ToBoolean(_configProvider.GetValue("XbmcCleanOnDownload", false)))
{
_logger.Trace("Sending Clean DB Request to XBMC");
_xbmcProvider.Clean();
}
}
_logger.Trace("XBMC Notifier is not enabled");
}
public override void OnRename(string message, Series series)
{
const string header = "NzbDrone [TV] - Renamed";
if (Convert.ToBoolean(_configProvider.GetValue("XbmcNotifyOnRename", false)))
{
_logger.Trace("Sending Notification to XBMC");
_xbmcProvider.Notify(header, message);
}
if (Convert.ToBoolean(_configProvider.GetValue("XbmcUpdateOnRename", false)))
{
_logger.Trace("Sending Update Request to XBMC");
_xbmcProvider.Update(series);
}
if (Convert.ToBoolean(_configProvider.GetValue("XbmcCleanOnRename", false)))
{
_logger.Trace("Sending Clean DB Request to XBMC");
_xbmcProvider.Clean();
}
}
}
}

@ -1,7 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Ninject; using Ninject;
using NLog; using NLog;
using NzbDrone.Core.Model;
using NzbDrone.Core.Providers.ExternalNotification; using NzbDrone.Core.Providers.ExternalNotification;
using NzbDrone.Core.Repository; using NzbDrone.Core.Repository;
using PetaPoco; using PetaPoco;
@ -13,10 +15,13 @@ namespace NzbDrone.Core.Providers
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly IDatabase _database; private readonly IDatabase _database;
[Inject] private IEnumerable<ExternalNotificationBase> _notifiers;
public ExternalNotificationProvider(IDatabase database)
[Inject]
public ExternalNotificationProvider(IDatabase database, IEnumerable<ExternalNotificationBase> notifiers)
{ {
_database = database; _database = database;
_notifiers = notifiers;
} }
public ExternalNotificationProvider() public ExternalNotificationProvider()
@ -24,51 +29,54 @@ namespace NzbDrone.Core.Providers
} }
public virtual List<ExternalNotificationSetting> All() public virtual List<ExternalNotificationDefinition> All()
{ {
return _database.Fetch<ExternalNotificationSetting>(); return _database.Fetch<ExternalNotificationDefinition>();
} }
public virtual void SaveSettings(ExternalNotificationSetting settings) public virtual void SaveSettings(ExternalNotificationDefinition settings)
{ {
if (settings.Id == 0) if (settings.Id == 0)
{ {
Logger.Debug("Adding External Notification settings for {0}", settings.Name); Logger.Debug("Adding External Notification definition for {0}", settings.Name);
_database.Insert(settings); _database.Insert(settings);
} }
else else
{ {
Logger.Debug("Updating External Notification settings for {0}", settings.Name); Logger.Debug("Updating External Notification definition for {0}", settings.Name);
_database.Update(settings); _database.Update(settings);
} }
} }
public virtual ExternalNotificationSetting GetSettings(Type type) public virtual ExternalNotificationDefinition GetSettings(Type type)
{ {
return _database.SingleOrDefault<ExternalNotificationSetting>("WHERE NotifierName = @0", type.ToString()); return _database.SingleOrDefault<ExternalNotificationDefinition>("WHERE ExternalNotificationProviderType = @0", type.ToString());
} }
public virtual ExternalNotificationSetting GetSettings(int id) public virtual IList<ExternalNotificationBase> GetEnabledExternalNotifiers()
{ {
return _database.SingleOrDefault<ExternalNotificationSetting>(id); var all = All();
return _notifiers.Where(i => all.Exists(c => c.ExternalNotificationProviderType == i.GetType().ToString() && c.Enable)).ToList();
} }
public virtual void InitializeNotifiers(IList<ExternalNotificationProviderBase> notifiers) public virtual void InitializeNotifiers(IList<ExternalNotificationBase> notifiers)
{ {
Logger.Info("Initializing notifiers. Count {0}", notifiers.Count); Logger.Info("Initializing notifiers. Count {0}", notifiers.Count);
_notifiers = notifiers;
var currentNotifiers = All(); var currentNotifiers = All();
foreach (var feedProvider in notifiers) foreach (var notificationProvider in notifiers)
{ {
ExternalNotificationProviderBase externalNotificationProviderLocal = feedProvider; ExternalNotificationBase externalNotificationProviderLocal = notificationProvider;
if (!currentNotifiers.Exists(c => c.NotifierName == externalNotificationProviderLocal.GetType().ToString())) if (!currentNotifiers.Exists(c => c.ExternalNotificationProviderType == externalNotificationProviderLocal.GetType().ToString()))
{ {
var settings = new ExternalNotificationSetting() var settings = new ExternalNotificationDefinition
{ {
Enabled = false, Enable = false,
NotifierName = externalNotificationProviderLocal.GetType().ToString(), ExternalNotificationProviderType = externalNotificationProviderLocal.GetType().ToString(),
Name = externalNotificationProviderLocal.Name Name = externalNotificationProviderLocal.Name
}; };
@ -76,5 +84,29 @@ namespace NzbDrone.Core.Providers
} }
} }
} }
public virtual void OnGrab(string message)
{
foreach (var notifier in _notifiers.Where(i => GetSettings(i.GetType()).Enable))
{
notifier.OnGrab(message);
}
}
public virtual void OnDownload(string message, Series series)
{
foreach (var notifier in _notifiers.Where(i => GetSettings(i.GetType()).Enable))
{
notifier.OnDownload(message, series);
}
}
public virtual void OnRename(string message, Series series)
{
foreach (var notifier in _notifiers.Where(i => GetSettings(i.GetType()).Enable))
{
notifier.OnRename(message, series);
}
}
} }
} }

@ -79,12 +79,12 @@ namespace NzbDrone.Core.Providers.Jobs
if (series == null) if (series == null)
{ {
Logger.Warn("Unable to Import new download, series doesn't exist in database."); Logger.Warn("Unable to Import new download [{0}], series doesn't exist in database.", subfolder);
return; continue;
} }
var importedFiles = _diskScanProvider.Scan(series, subfolder); var importedFiles = _diskScanProvider.Scan(series, subfolder);
importedFiles.ForEach(file => _diskScanProvider.MoveEpisodeFile(file)); importedFiles.ForEach(file => _diskScanProvider.MoveEpisodeFile(file, true));
//Delete the folder only if folder is small enough //Delete the folder only if folder is small enough
if (_diskProvider.GetDirectorySize(subfolder) < 10.Megabytes()) if (_diskProvider.GetDirectorySize(subfolder) < 10.Megabytes())

@ -178,7 +178,7 @@ namespace NzbDrone.Core.Providers
public virtual bool SeriesPathExists(string cleanPath) public virtual bool SeriesPathExists(string cleanPath)
{ {
if (GetAllSeries().Any(s => s.Path == cleanPath)) if (GetAllSeries().Any(s => s.Path.ToLower() == cleanPath.ToLower()))
return true; return true;
return false; return false;

@ -0,0 +1,17 @@
using PetaPoco;
namespace NzbDrone.Core.Repository
{
[TableName("ExternalNotificationDefinitions")]
[PrimaryKey("Id", autoIncrement = true)]
public class ExternalNotificationDefinition
{
public int Id { get; set; }
public bool Enable { get; set; }
public string ExternalNotificationProviderType { get; set; }
public string Name { get; set; }
}
}

@ -1,17 +0,0 @@
using PetaPoco;
namespace NzbDrone.Core.Repository
{
[TableName("ExternalNotificationSettings")]
[PrimaryKey("Id", autoIncrement = true)]
public class ExternalNotificationSetting
{
public int Id { get; set; }
public bool Enabled { get; set; }
public string NotifierName { get; set; }
public string Name { get; set; }
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

@ -0,0 +1,20 @@
#sub-menu
{
padding-left: 5px;
}
#sub-menu li
{
display: inline;
list-style-type: none;
padding-left: 8px;
padding-right: 12px;
padding-top: 6px;
border-right: 1px solid #F0F0F0;
}
#sub-menu a
{
text-decoration: none;
color: #105CD6;
}

@ -108,3 +108,11 @@ p, h1, form, button{border:0; margin:0; padding:0;}
height: 20px; height: 20px;
display: none; display: none;
} }
#save_button[disabled="disabled"]
{
padding: 0px 6px 0px 6px;
border: 2px outset ButtonFace;
color: lightgrey;
cursor: progress;
}

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using NzbDrone.Core.Providers.Jobs;
namespace NzbDrone.Web.Controllers
{
public class CommandController : Controller
{
private readonly JobProvider _jobProvider;
public CommandController(JobProvider jobProvider)
{
_jobProvider = jobProvider;
}
public JsonResult RssSync()
{
_jobProvider.QueueJob(typeof(RssSyncJob));
return new JsonResult { Data = "ok" };
}
public JsonResult SyncEpisodesOnDisk(int seriesId)
{
//Syncs the episodes on disk for the specified series
_jobProvider.QueueJob(typeof(DiskScanJob), seriesId);
return new JsonResult { Data = "ok" };
}
public JsonResult UpdateInfo(int seriesId)
{
//Syncs the episodes on disk for the specified series
_jobProvider.QueueJob(typeof(UpdateInfoJob), seriesId);
return new JsonResult { Data = "ok" };
}
public JsonResult RenameSeries(int seriesId)
{
//Syncs the episodes on disk for the specified series
//_jobProvider.QueueJob(typeof(UpdateInfoJob), seriesId);
return new JsonResult { Data = "ok" };
}
}
}

@ -26,16 +26,16 @@ namespace NzbDrone.Web.Controllers
return View(); return View();
} }
public ActionResult Trim() public JsonResult Trim()
{ {
_historyProvider.Trim(); _historyProvider.Trim();
return RedirectToAction("Index"); return new JsonResult { Data = "ok" };
} }
public ActionResult Purge() public JsonResult Purge()
{ {
_historyProvider.Purge(); _historyProvider.Purge();
return RedirectToAction("Index"); return new JsonResult { Data = "ok" };
} }
[GridAction] [GridAction]

@ -18,11 +18,11 @@ namespace NzbDrone.Web.Controllers
return View(); return View();
} }
public JsonResult Clear()
public ActionResult Clear()
{ {
_logProvider.DeleteAll(); _logProvider.DeleteAll();
return RedirectToAction("Index");
return new JsonResult { Data = "ok" };
} }
[GridAction] [GridAction]

@ -26,10 +26,9 @@ namespace NzbDrone.Web.Controllers
[GridAction] [GridAction]
public ActionResult _AjaxBinding() public ActionResult _AjaxBinding()
{ {
//TODO: possible subsonic bug, IQuarible causes some issues so ToList() is called var missingEpisodes = _episodeProvider.EpisodesWithoutFiles(false);
//https://github.com/subsonic/SubSonic-3.0/issues/263
var missing = _episodeProvider.EpisodesWithoutFiles(true).Select(e => new MissingEpisodeModel var missing = missingEpisodes.Select(e => new MissingEpisodeModel
{ {
EpisodeId = e.EpisodeId, EpisodeId = e.EpisodeId,
SeasonNumber = e.SeasonNumber, SeasonNumber = e.SeasonNumber,

@ -67,6 +67,8 @@ namespace NzbDrone.Web.Controllers
private string GetCurrentMessage() private string GetCurrentMessage()
{ {
var notes = _notifications.ProgressNotifications;
if (_notifications.ProgressNotifications.Count != 0) if (_notifications.ProgressNotifications.Count != 0)
return _notifications.ProgressNotifications[0].CurrentMessage; return _notifications.ProgressNotifications[0].CurrentMessage;

@ -47,12 +47,6 @@ namespace NzbDrone.Web.Controllers
return View(); return View();
} }
public ActionResult RssSync()
{
_jobProvider.QueueJob(typeof(RssSyncJob));
return RedirectToAction("Index");
}
public ActionResult SeasonEditor(int seriesId) public ActionResult SeasonEditor(int seriesId)
{ {
var model = new List<SeasonEditModel>(); var model = new List<SeasonEditModel>();
@ -148,12 +142,17 @@ namespace NzbDrone.Web.Controllers
} }
[HttpPost] [HttpPost]
public JsonResult SaveSeason(int seriesId, int seasonNumber, bool monitored) public JsonResult SaveSeasonIgnore(int seriesId, int seasonNumber, bool ignored)
{ {
if (_episodeProvider.IsIgnored(seriesId, seasonNumber) == monitored) _episodeProvider.SetSeasonIgnore(seriesId, seasonNumber, ignored);
{
_episodeProvider.SetSeasonIgnore(seriesId, seasonNumber, !monitored); return new JsonResult { Data = "ok" };
} }
[HttpPost]
public JsonResult SaveEpisodeIgnore(int episodeId, bool ignored)
{
_episodeProvider.SetEpisodeIgnore(episodeId, ignored);
return new JsonResult { Data = "ok" }; return new JsonResult { Data = "ok" };
} }
@ -180,21 +179,6 @@ namespace NzbDrone.Web.Controllers
return View(model); return View(model);
} }
public ActionResult SyncEpisodesOnDisk(int seriesId)
{
//Syncs the episodes on disk for the specified series
_jobProvider.QueueJob(typeof(DiskScanJob), seriesId);
return RedirectToAction("Details", new { seriesId });
}
public ActionResult UpdateInfo(int seriesId)
{
//Syncs the episodes on disk for the specified series
_jobProvider.QueueJob(typeof(UpdateInfoJob), seriesId);
return RedirectToAction("Details", new { seriesId });
}
private List<SeriesModel> GetSeriesModels(IList<Series> seriesInDb) private List<SeriesModel> GetSeriesModels(IList<Series> seriesInDb)
{ {
var series = seriesInDb.Select(s => new SeriesModel var series = seriesInDb.Select(s => new SeriesModel
@ -227,8 +211,6 @@ namespace NzbDrone.Web.Controllers
var episodePath = String.Empty; var episodePath = String.Empty;
var episodeQuality = String.Empty; var episodeQuality = String.Empty;
if (e.EpisodeFile != null) if (e.EpisodeFile != null)
{ {
episodePath = e.EpisodeFile.Path; episodePath = e.EpisodeFile.Path;
@ -252,7 +234,8 @@ namespace NzbDrone.Web.Controllers
Path = episodePath, Path = episodePath,
EpisodeFileId = episodeFileId, EpisodeFileId = episodeFileId,
Status = e.Status.ToString(), Status = e.Status.ToString(),
Quality = episodeQuality Quality = episodeQuality,
Ignored = e.Ignored
}); });
} }

@ -9,6 +9,7 @@ using NzbDrone.Core.Model;
using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Model.Notification;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Providers.ExternalNotification;
using NzbDrone.Core.Providers.Indexer; using NzbDrone.Core.Providers.Indexer;
using NzbDrone.Core.Repository; using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality; using NzbDrone.Core.Repository.Quality;
@ -30,12 +31,16 @@ namespace NzbDrone.Web.Controllers
private readonly NotificationProvider _notificationProvider; private readonly NotificationProvider _notificationProvider;
private readonly DiskProvider _diskProvider; private readonly DiskProvider _diskProvider;
private readonly SeriesProvider _seriesProvider; private readonly SeriesProvider _seriesProvider;
private readonly ExternalNotificationProvider _externalNotificationProvider;
private readonly ProgressNotification _progressNotification;
public SettingsController(ConfigProvider configProvider, IndexerProvider indexerProvider, public SettingsController(ConfigProvider configProvider, IndexerProvider indexerProvider,
QualityProvider qualityProvider, RootDirProvider rootDirProvider, QualityProvider qualityProvider, RootDirProvider rootDirProvider,
AutoConfigureProvider autoConfigureProvider, NotificationProvider notificationProvider, AutoConfigureProvider autoConfigureProvider, NotificationProvider notificationProvider,
DiskProvider diskProvider, SeriesProvider seriesProvider) DiskProvider diskProvider, SeriesProvider seriesProvider,
ExternalNotificationProvider externalNotificationProvider)
{ {
_externalNotificationProvider = externalNotificationProvider;
_configProvider = configProvider; _configProvider = configProvider;
_indexerProvider = indexerProvider; _indexerProvider = indexerProvider;
_qualityProvider = qualityProvider; _qualityProvider = qualityProvider;
@ -44,6 +49,8 @@ namespace NzbDrone.Web.Controllers
_notificationProvider = notificationProvider; _notificationProvider = notificationProvider;
_diskProvider = diskProvider; _diskProvider = diskProvider;
_seriesProvider = seriesProvider; _seriesProvider = seriesProvider;
_progressNotification = new ProgressNotification("Settings");
} }
public ActionResult Test() public ActionResult Test()
@ -140,7 +147,7 @@ namespace NzbDrone.Web.Controllers
{ {
var model = new NotificationSettingsModel var model = new NotificationSettingsModel
{ {
XbmcEnabled = _configProvider.XbmcEnabled, XbmcEnabled = _externalNotificationProvider.GetSettings(typeof(Xbmc)).Enable,
XbmcNotifyOnGrab = _configProvider.XbmcNotifyOnGrab, XbmcNotifyOnGrab = _configProvider.XbmcNotifyOnGrab,
XbmcNotifyOnDownload = _configProvider.XbmcNotifyOnDownload, XbmcNotifyOnDownload = _configProvider.XbmcNotifyOnDownload,
XbmcUpdateLibrary = _configProvider.XbmcUpdateLibrary, XbmcUpdateLibrary = _configProvider.XbmcUpdateLibrary,
@ -263,13 +270,10 @@ namespace NzbDrone.Web.Controllers
} }
} }
[HttpPost] [HttpPost]
public ActionResult SaveIndexers(IndexerSettingsModel data) public ActionResult SaveIndexers(IndexerSettingsModel data)
{ {
var basicNotification = new BasicNotification(); _notificationProvider.Register(_progressNotification);
basicNotification.Type = BasicNotificationType.Info;
basicNotification.AutoDismiss = true;
if (ModelState.IsValid) if (ModelState.IsValid)
{ {
@ -301,22 +305,20 @@ namespace NzbDrone.Web.Controllers
_configProvider.NewzbinUsername = data.NewzbinUsername; _configProvider.NewzbinUsername = data.NewzbinUsername;
_configProvider.NewzbinPassword = data.NewzbinPassword; _configProvider.NewzbinPassword = data.NewzbinPassword;
basicNotification.Title = SETTINGS_SAVED; _progressNotification.CurrentMessage = SETTINGS_SAVED;
_notificationProvider.Register(basicNotification); _progressNotification.Status = ProgressNotificationStatus.Completed;
return Content(SETTINGS_SAVED); return Content(SETTINGS_SAVED);
} }
basicNotification.Title = SETTINGS_FAILED; _progressNotification.CurrentMessage = SETTINGS_FAILED;
_notificationProvider.Register(basicNotification); _progressNotification.Status = ProgressNotificationStatus.Completed;
return Content(SETTINGS_FAILED); return Content(SETTINGS_FAILED);
} }
[HttpPost] [HttpPost]
public ActionResult SaveSabnzbd(SabnzbdSettingsModel data) public ActionResult SaveSabnzbd(SabnzbdSettingsModel data)
{ {
var basicNotification = new BasicNotification(); _notificationProvider.Register(_progressNotification);
basicNotification.Type = BasicNotificationType.Info;
basicNotification.AutoDismiss = true;
if (ModelState.IsValid) if (ModelState.IsValid)
{ {
@ -329,22 +331,20 @@ namespace NzbDrone.Web.Controllers
_configProvider.SabTvPriority = data.SabTvPriority; _configProvider.SabTvPriority = data.SabTvPriority;
_configProvider.SabDropDirectory = data.SabDropDirectory; _configProvider.SabDropDirectory = data.SabDropDirectory;
basicNotification.Title = SETTINGS_SAVED; _progressNotification.CurrentMessage = SETTINGS_SAVED;
_notificationProvider.Register(basicNotification); _progressNotification.Status = ProgressNotificationStatus.Completed;
return Content(SETTINGS_SAVED); return Content(SETTINGS_SAVED);
} }
basicNotification.Title = SETTINGS_FAILED; _progressNotification.CurrentMessage = SETTINGS_FAILED;
_notificationProvider.Register(basicNotification); _progressNotification.Status = ProgressNotificationStatus.Completed;
return Content(SETTINGS_FAILED); return Content(SETTINGS_FAILED);
} }
[HttpPost] [HttpPost]
public ActionResult SaveQuality(QualityModel data) public ActionResult SaveQuality(QualityModel data)
{ {
var basicNotification = new BasicNotification(); _notificationProvider.Register(_progressNotification);
basicNotification.Type = BasicNotificationType.Info;
basicNotification.AutoDismiss = true;
if (ModelState.IsValid) if (ModelState.IsValid)
{ {
@ -376,26 +376,29 @@ namespace NzbDrone.Web.Controllers
_qualityProvider.Update(profile); _qualityProvider.Update(profile);
} }
basicNotification.Title = SETTINGS_SAVED;
_notificationProvider.Register(basicNotification); _progressNotification.CurrentMessage = SETTINGS_SAVED;
_progressNotification.Status = ProgressNotificationStatus.Completed;
return Content(SETTINGS_SAVED); return Content(SETTINGS_SAVED);
} }
basicNotification.Title = SETTINGS_FAILED; _progressNotification.CurrentMessage = SETTINGS_FAILED;
_notificationProvider.Register(basicNotification); _progressNotification.Status = ProgressNotificationStatus.Completed;
return Content(SETTINGS_FAILED); return Content(SETTINGS_FAILED);
} }
[HttpPost] [HttpPost]
public ActionResult SaveNotifications(NotificationSettingsModel data) public ActionResult SaveNotifications(NotificationSettingsModel data)
{ {
var basicNotification = new BasicNotification(); _notificationProvider.Register(_progressNotification);
basicNotification.Type = BasicNotificationType.Info;
basicNotification.AutoDismiss = true;
if (ModelState.IsValid) if (ModelState.IsValid)
{ {
_configProvider.XbmcEnabled = data.XbmcEnabled; //XBMC Enabled
var xbmcSettings = _externalNotificationProvider.GetSettings(typeof(Xbmc));
xbmcSettings.Enable = data.XbmcEnabled;
_externalNotificationProvider.SaveSettings(xbmcSettings);
_configProvider.XbmcNotifyOnGrab = data.XbmcNotifyOnGrab; _configProvider.XbmcNotifyOnGrab = data.XbmcNotifyOnGrab;
_configProvider.XbmcNotifyOnDownload = data.XbmcNotifyOnDownload; _configProvider.XbmcNotifyOnDownload = data.XbmcNotifyOnDownload;
_configProvider.XbmcUpdateLibrary = data.XbmcUpdateLibrary; _configProvider.XbmcUpdateLibrary = data.XbmcUpdateLibrary;
@ -404,22 +407,20 @@ namespace NzbDrone.Web.Controllers
_configProvider.XbmcUsername = data.XbmcUsername; _configProvider.XbmcUsername = data.XbmcUsername;
_configProvider.XbmcPassword = data.XbmcPassword; _configProvider.XbmcPassword = data.XbmcPassword;
basicNotification.Title = SETTINGS_SAVED; _progressNotification.CurrentMessage = SETTINGS_SAVED;
_notificationProvider.Register(basicNotification); _progressNotification.Status = ProgressNotificationStatus.Completed;
return Content(SETTINGS_SAVED); return Content(SETTINGS_SAVED);
} }
basicNotification.Title = SETTINGS_FAILED; _progressNotification.CurrentMessage = SETTINGS_FAILED;
_notificationProvider.Register(basicNotification); _progressNotification.Status = ProgressNotificationStatus.Completed;
return Content(SETTINGS_FAILED); return Content(SETTINGS_FAILED);
} }
[HttpPost] [HttpPost]
public ActionResult SaveEpisodeSorting(EpisodeSortingModel data) public ActionResult SaveEpisodeSorting(EpisodeSortingModel data)
{ {
var basicNotification = new BasicNotification(); _notificationProvider.Register(_progressNotification);
basicNotification.Type = BasicNotificationType.Info;
basicNotification.AutoDismiss = true;
if (ModelState.IsValid) if (ModelState.IsValid)
{ {
@ -433,13 +434,13 @@ namespace NzbDrone.Web.Controllers
_configProvider.SortingNumberStyle = data.NumberStyle; _configProvider.SortingNumberStyle = data.NumberStyle;
_configProvider.SortingMultiEpisodeStyle = data.MultiEpisodeStyle; _configProvider.SortingMultiEpisodeStyle = data.MultiEpisodeStyle;
basicNotification.Title = SETTINGS_SAVED; _progressNotification.CurrentMessage = SETTINGS_SAVED;
_notificationProvider.Register(basicNotification); _progressNotification.Status = ProgressNotificationStatus.Completed;
return Content(SETTINGS_SAVED); return Content(SETTINGS_SAVED);
} }
basicNotification.Title = SETTINGS_FAILED; _progressNotification.CurrentMessage = SETTINGS_FAILED;
_notificationProvider.Register(basicNotification); _progressNotification.Status = ProgressNotificationStatus.Completed;
return Content(SETTINGS_FAILED); return Content(SETTINGS_FAILED);
} }
} }

@ -15,5 +15,6 @@ namespace NzbDrone.Web.Models
public String Status { get; set; } public String Status { get; set; }
public string AirDate { get; set; } public string AirDate { get; set; }
public String Quality { get; set; } public String Quality { get; set; }
public bool Ignored { get; set; }
} }
} }

@ -26,16 +26,20 @@ namespace NzbDrone.Web.Models
//View & Edit //View & Edit
[DisplayName("Path")] [DisplayName("Path")]
[Description("Where should NzbDrone store episodes for this series?")]
public string Path { get; set; } public string Path { get; set; }
[DisplayName("Quality Profile")] [DisplayName("Quality Profile")]
[Description("Which Quality Profile should NzbDrone use to download episodes?")]
public virtual int QualityProfileId { get; set; } public virtual int QualityProfileId { get; set; }
//Editing Only //Editing Only
[DisplayName("Use Season Folder")] [DisplayName("Use Season Folder")]
[Description("Should downloaded episodes be stored in season folders?")]
public bool SeasonFolder { get; set; } public bool SeasonFolder { get; set; }
[DisplayName("Monitored")] [DisplayName("Monitored")]
[Description("Should NzbDrone download episodes for this series?")]
public bool Monitored { get; set; } public bool Monitored { get; set; }
[DisplayName("Season Editor")] [DisplayName("Season Editor")]

@ -135,6 +135,8 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Content\Menu.css" />
<Compile Include="Controllers\CommandController.cs" />
<Compile Include="Controllers\DirectoryController.cs" /> <Compile Include="Controllers\DirectoryController.cs" />
<Compile Include="Controllers\EpisodeController.cs" /> <Compile Include="Controllers\EpisodeController.cs" />
<Compile Include="Controllers\HealthController.cs" /> <Compile Include="Controllers\HealthController.cs" />
@ -183,6 +185,9 @@
<Content Include="Content\Blueprint\ie.css" /> <Content Include="Content\Blueprint\ie.css" />
<Content Include="Content\Blueprint\screen.css" /> <Content Include="Content\Blueprint\screen.css" />
<Content Include="Content\Blueprint\liquid.css" /> <Content Include="Content\Blueprint\liquid.css" />
<Content Include="Content\Images\ignored.png" />
<Content Include="Content\Images\ignoredNeutral.png" />
<Content Include="Content\Images\notIgnored.png" />
<Content Include="Content\Images\x_16.png" /> <Content Include="Content\Images\x_16.png" />
<Content Include="Content\jQueryUI\images\ui-bg_diagonals-thick_30_a32d00_40x40.png" /> <Content Include="Content\jQueryUI\images\ui-bg_diagonals-thick_30_a32d00_40x40.png" />
<Content Include="Content\jQueryUI\images\ui-bg_flat_0_065efe_40x100.png" /> <Content Include="Content\jQueryUI\images\ui-bg_flat_0_065efe_40x100.png" />
@ -250,6 +255,7 @@
<Content Include="favicon.ico" /> <Content Include="favicon.ico" />
<Content Include="Global.asax" /> <Content Include="Global.asax" />
<Content Include="Scripts\AutoComplete.js" /> <Content Include="Scripts\AutoComplete.js" />
<Content Include="Scripts\seriesDetails.js" />
<Content Include="Scripts\Plugins\jquery.livequery.js" /> <Content Include="Scripts\Plugins\jquery.livequery.js" />
<Content Include="Scripts\settingsForm.js" /> <Content Include="Scripts\settingsForm.js" />
<Content Include="Scripts\Plugins\doTimeout.js" /> <Content Include="Scripts\Plugins\doTimeout.js" />
@ -283,11 +289,10 @@
<Content Include="Views\Shared\Footer.cshtml" /> <Content Include="Views\Shared\Footer.cshtml" />
<Content Include="Views\_ViewStart.cshtml" /> <Content Include="Views\_ViewStart.cshtml" />
<Content Include="Views\History\Index.cshtml" /> <Content Include="Views\History\Index.cshtml" />
<Content Include="Views\Log\index.cshtml" /> <Content Include="Views\Log\Index.cshtml" />
<Content Include="Views\Upcoming\Index.cshtml" /> <Content Include="Views\Upcoming\Index.cshtml" />
<Content Include="Views\Series\Details.cshtml" /> <Content Include="Views\Series\Details.cshtml" />
<Content Include="Views\Series\Index.cshtml" /> <Content Include="Views\Series\Index.cshtml" />
<Content Include="Views\Series\SubMenu.cshtml" />
<Content Include="Views\Series\SeriesSearchResults.cshtml" /> <Content Include="Views\Series\SeriesSearchResults.cshtml" />
<Content Include="Views\Shared\Error.cshtml" /> <Content Include="Views\Shared\Error.cshtml" />
<Content Include="Views\Settings\QualityProfileItem.cshtml" /> <Content Include="Views\Settings\QualityProfileItem.cshtml" />
@ -317,9 +322,6 @@
<ItemGroup> <ItemGroup>
<Content Include="Views\Series\EditorTemplates\SeriesModel.cshtml" /> <Content Include="Views\Series\EditorTemplates\SeriesModel.cshtml" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="Views\Series\SeasonEditor.cshtml" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Views\Series\SingleSeason.cshtml" /> <Content Include="Views\Series\SingleSeason.cshtml" />
</ItemGroup> </ItemGroup>

@ -0,0 +1,136 @@
var notIgnoredImage = '../../Content/Images/notIgnored.png';
var ignoredImage = '../../Content/Images/ignored.png';
var seriesId = 0;
var saveSeasonIgnoreUrl = '../Series/SaveSeasonIgnore';
var saveEpisodeIgnoreUrl = '../Series/SaveEpisodeIgnore';
$(".ignoreEpisode").live("click", function () {
var toggle = $(this);
var ignored = toggle.hasClass('ignored');
if (ignored) {
toggle.removeClass('ignored');
toggle.attr('src', notIgnoredImage);
}
else {
toggle.addClass('ignored');
toggle.attr('src', ignoredImage);
}
var seasonNumber = 0;
//Flip the ignored to the new state (We want the new value moving forward)
ignored = !ignored;
if (toggle.hasClass('ignoredEpisodesMaster')) {
seasonNumber = toggle.attr('id').replace('master_', '');
toggleChildren(seasonNumber, ignored);
saveSeasonIgnore(seasonNumber, ignored);
}
else {
//Check to see if this is the last one ignored or the first not ignored
seasonNumber = toggle.attr('class').split(/\s+/)[1].replace('ignoreEpisode_', '');
var episodeId = toggle.attr('id');
toggleMaster(seasonNumber, ignored);
saveEpisodeIgnore(episodeId, ignored);
}
});
function toggleChildren(seasonNumber, ignored) {
var ignoreEpisodes = $('.ignoreEpisode_' + seasonNumber);
if (ignored) {
ignoreEpisodes.each(function (index) {
$(this).addClass('ignored');
$(this).attr('src', ignoredImage);
});
}
else {
ignoreEpisodes.each(function (index) {
$(this).removeClass('ignored');
$(this).attr('src', notIgnoredImage);
});
}
}
function toggleMaster(seasonNumber) {
var ignoreEpisodes = $('.ignoreEpisode_' + seasonNumber);
var ignoredCount = ignoreEpisodes.filter('.ignored').length;
var master = $('#master_' + seasonNumber);
if (ignoreEpisodes.length == ignoredCount) {
master.attr('src', ignoredImage);
master.addClass('ignored');
}
else {
master.attr('src', notIgnoredImage);
master.removeClass('ignored');
}
}
//Functions called by the Telerik Season Grid
function grid_rowBound(e) {
var dataItem = e.dataItem;
var ignored = dataItem.Ignored;
var episodeId = dataItem.EpisodeId;
var ignoredIcon = $('#' + episodeId);
if (ignored) {
ignoredIcon.attr('src', ignoredImage);
}
else {
ignoredIcon.attr('src', notIgnoredImage);
ignoredIcon.removeClass('ignored');
}
if (seriesId == 0)
seriesId = dataItem.SeriesId
}
function grid_dataBound(e) {
var id = $(this).attr('id');
var seasonNumber = id.replace('seasons_', '');
var ignoreEpisodes = $('.ignoreEpisode_' + seasonNumber);
var master = $('#master_' + seasonNumber);
var count = ignoreEpisodes.length;
var ignoredCount = ignoreEpisodes.filter('.ignored').length;
if (ignoredCount == count) {
master.attr('src', ignoredImage);
master.addClass('ignored');
}
else {
master.attr('src', notIgnoredImage);
master.removeClass('ignored');
}
}
function saveSeasonIgnore(seasonNumber, ignored) {
$.ajax({
type: "POST",
url: saveSeasonIgnoreUrl,
data: jQuery.param({ seriesId: seriesId, seasonNumber: seasonNumber, ignored: ignored }),
error: function (req, status, error) {
alert("Sorry! We could save the ignore settings for Series: " + seriesId + ", Season: " + seasonNumber + " at this time. " + error);
}
});
}
function saveEpisodeIgnore(episodeId, ignored) {
$.ajax({
type: "POST",
url: saveEpisodeIgnoreUrl,
data: jQuery.param({ episodeId: episodeId, ignored: ignored }),
error: function (req, status, error) {
alert("Sorry! We could save the ignore settings for Episode: " + episodeId + " at this time. " + error);
}
});
}

@ -7,6 +7,7 @@
resetForm: false resetForm: false
}; };
$('#form').ajaxForm(options); $('#form').ajaxForm(options);
$('#save_button').removeAttr('disabled');
}); });
function showRequest(formData, jqForm, options) { function showRequest(formData, jqForm, options) {

@ -4,11 +4,10 @@
History History
} }
@section ActionMenu{ @section ActionMenu{
@{Html.Telerik().Menu().Name("historyMenu").Items(items => <ul id="sub-menu">
{ <li>@Ajax.ActionLink("Trim History", "Trim", "History", new AjaxOptions{ OnSuccess = "reloadGrid" })</li>
items.Add().Text("Trim History").Action("Trim", "History"); <li>@Ajax.ActionLink("Purge History", "Purge", "History", new AjaxOptions{ OnSuccess = "reloadGrid" })</li>
items.Add().Text("Purge History").Action("Purge", "History"); </ul>
}).Render();}
} }
@section MainContent{ @section MainContent{
<div class="grid-container"> <div class="grid-container">
@ -42,3 +41,12 @@ History
.Render();} .Render();}
</div> </div>
} }
<script type="text/javascript">
function reloadGrid() {
var grid = $('#history').data('tGrid');
grid.rebind();
}
</script>

@ -22,10 +22,13 @@
@section TitleContent{ @section TitleContent{
Logs Logs
} }
@section ActionMenu{ @section ActionMenu{
@{Html.Telerik().Menu().Name("logMenu").Items(items => items.Add().Text("Clear Logs").Action("Clear", "Log")) <ul id="sub-menu">
.Render();} <li>@Ajax.ActionLink("Clear Logs", "Clear", "Log", new AjaxOptions{ OnSuccess = "reloadGrid" })</li>
</ul>
} }
@section MainContent{ @section MainContent{
@{Html.Telerik().Grid(Model).Name("logsGrid") @{Html.Telerik().Grid(Model).Name("logsGrid")
.TableHtmlAttributes(new { @class = "Grid" }) .TableHtmlAttributes(new { @class = "Grid" })
@ -47,3 +50,10 @@ Logs
.ClientEvents(c => c.OnRowDataBound("onRowDataBound")) .ClientEvents(c => c.OnRowDataBound("onRowDataBound"))
.Render();} .Render();}
} }
<script type="text/javascript">
function reloadGrid() {
var grid = $('#logsGrid').data('tGrid');
grid.rebind();
}
</script>

@ -4,44 +4,37 @@
@section TitleContent{ @section TitleContent{
@Model.Title @Model.Title
} }
<script src="../../Scripts/seriesDetails.js" type="text/javascript"></script>
<style>
.ignoreEpisode
{
width: 18px;
height: 18px;
padding-bottom: -6px;
}
.ignoredEpisodesMaster
{
width: 18px;
height: 18px;
padding-left: 18px;
padding-right: -18px;
}
</style>
@section ActionMenu{ @section ActionMenu{
@{Html.Telerik().Menu().Name("SeriesMenu").Items(items => <ul id="sub-menu">
{ <li>@Html.ActionLink("Back to Series List", "Index", "Series")</li>
items.Add().Text("Back to Series List").Action("Index", "Series"); <li>@Ajax.ActionLink("Scan For Episodes on Disk", "SyncEpisodesOnDisk", "Command", new { seriesId = Model.SeriesId }, null)</li>
items.Add().Text("Scan For Episodes on Disk") <li>@Ajax.ActionLink("Update Info", "UpdateInfo", "Command", new { seriesId = Model.SeriesId }, null)</li>
.Action("SyncEpisodesOnDisk", "Series", new { seriesId = Model.SeriesId }); <li>@Ajax.ActionLink("Rename Series", "RenameSeries", "Command", new { seriesId = Model.SeriesId }, null)</li>
items.Add().Text("Update Info").Action("UpdateInfo", "Series", new { seriesId = Model.SeriesId }); </ul>
items.Add().Text("Rename Series").Action("RenameSeries", "Series", new { seriesId = Model.SeriesId });
}).Render();}
} }
@section MainContent{ @section MainContent{
<fieldset>
<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.AirsDayOfWeek</div>
<div class="display-label">
Language</div>
<div class="display-label">
Location</div>
<div class="display-field">
@Model.Path</div>
</fieldset>
@foreach (var season in Model.Seasons.Where(s => s > 0).Reverse()) @foreach (var season in Model.Seasons.Where(s => s > 0).Reverse())
{ {
<br />
<h3> <h3>
Season @season</h3> Season @season</h3>
<div class="grid-container"> <div class="grid-container">
@ -50,11 +43,11 @@
.TableHtmlAttributes(new { @class = "Grid" }) .TableHtmlAttributes(new { @class = "Grid" })
.Columns(columns => .Columns(columns =>
{ {
columns.Bound(o => o.EpisodeId) columns.Bound(o => o.Ignored)
.Title("<img src='../../Content/Images/ignoredNeutral.png' class='ignoredEpisodesMaster ignoreEpisode' id='master_" + season + "' />")
.ClientTemplate( .ClientTemplate(
"<input type='checkbox' name='checkedEpisodes' value='<#= EpisodeId #>' />") "<img src='../../Content/Images/ignoredNeutral.png' class='ignoreEpisode ignoreEpisode_" + season + " ignored' id='<#= EpisodeId #>' />")
.Title("") .Width(20)
.Width(1)
.HtmlAttributes(new { style = "text-align:center" }); .HtmlAttributes(new { style = "text-align:center" });
columns.Bound(c => c.EpisodeNumber).Width(0).Title("Episode"); columns.Bound(c => c.EpisodeNumber).Width(0).Title("Episode");
@ -68,7 +61,7 @@
"<a href='#Rename' onClick=\"renameEpisode('<#= EpisodeFileId #>'); return false;\">Rename</a>"); "<a href='#Rename' onClick=\"renameEpisode('<#= EpisodeFileId #>'); return false;\">Rename</a>");
}) })
.DetailView(detailView => detailView.ClientTemplate("<div><#= Overview #> </br><#= Path #> </div>")) .DetailView(detailView => detailView.ClientTemplate("<div><#= Overview #> </br><#= Path #> </div>"))
.Sortable(rows => rows.OrderBy(epSort => epSort.Add(c => c.EpisodeNumber).Descending()).Enabled(true)) .Sortable(rows => rows.OrderBy(epSort => epSort.Add(c => c.EpisodeNumber).Descending()).Enabled(false))
.Footer(true) .Footer(true)
.DataBinding( .DataBinding(
d => d =>
@ -78,12 +71,16 @@
c => c =>
c.Custom().Text("Rename Season").Action("RenameSeason", "Series", new { seasonId = season }) c.Custom().Text("Rename Season").Action("RenameSeason", "Series", new { seasonId = season })
.ButtonType(GridButtonType.Text)) .ButtonType(GridButtonType.Text))
.ClientEvents(clientEvents =>
{
clientEvents.OnRowDataBound("grid_rowBound");
clientEvents.OnDataBound("grid_dataBound");
})
.Render();} .Render();}
</div> </div>
} }
@if (Model.Seasons.Any(s => s == 0)) @if (Model.Seasons.Any(s => s == 0))
{ {
<br /> <br />
<h3> <h3>
Specials</h3> Specials</h3>
@ -92,11 +89,11 @@
.TableHtmlAttributes(new { @class = "Grid" }) .TableHtmlAttributes(new { @class = "Grid" })
.Columns(columns => .Columns(columns =>
{ {
columns.Bound(o => o.EpisodeId) columns.Bound(o => o.Ignored)
.Title("<img src='../../Content/Images/ignoredNeutral.png' class='ignoredEpisodesMaster ignoreEpisode' id='master_0' />")
.ClientTemplate( .ClientTemplate(
"<input type='checkbox' name='checkedEpisodes' value='<#= EpisodeId #>' />") "<img src='../../Content/Images/ignoredNeutral.png' class='ignoreEpisode ignoreEpisode_0 ignored' id='<#= EpisodeId #>' />")
.Title("") .Width(20)
.Width(1)
.HtmlAttributes(new { style = "text-align:center" }); .HtmlAttributes(new { style = "text-align:center" });
columns.Bound(c => c.EpisodeNumber).Width(10).Title("Episode"); columns.Bound(c => c.EpisodeNumber).Width(10).Title("Episode");
@ -106,7 +103,7 @@
columns.Bound(c => c.Status).Width(10); columns.Bound(c => c.Status).Width(10);
}) })
.DetailView(detailView => detailView.ClientTemplate("<div><#= Overview #> </br><#= Path #> </div>")) .DetailView(detailView => detailView.ClientTemplate("<div><#= Overview #> </br><#= Path #> </div>"))
.Sortable(rows => rows.OrderBy(epSort => epSort.Add(c => c.EpisodeNumber).Descending()).Enabled(true)) .Sortable(rows => rows.OrderBy(epSort => epSort.Add(c => c.EpisodeNumber).Descending()).Enabled(false))
.Footer(true) .Footer(true)
.DataBinding( .DataBinding(
d => d =>
@ -135,5 +132,7 @@
}); });
} }
seriesId = @Model.SeriesId;
</script> </script>
} }

@ -5,31 +5,29 @@
Layout = null; Layout = null;
} }
<div class=".settingsForm"> <link rel="stylesheet" type="text/css" href="../../../Content/Settings.css" />
@Html.HiddenFor(m => m.SeriesId)
<label class="labelClass">@Html.LabelFor(m => m.Monitored)
<span class="small">@Html.DescriptionFor(m => m.Monitored)</span>
</label>
@Html.CheckBoxFor(m => m.Monitored, new { @class = "inputClass checkClass" })
<label class="labelClass">@Html.LabelFor(m => m.SeasonFolder)
<span class="small">@Html.DescriptionFor(m => m.SeasonFolder)</span>
</label>
@Html.CheckBoxFor(m => m.SeasonFolder, new { @class = "inputClass checkClass" })
<label class="labelClass">@Html.LabelFor(m => m.QualityProfileId)
<span class="small">@Html.DescriptionFor(m => m.QualityProfileId)</span>
</label>
@Html.DropDownListFor(m => m.QualityProfileId, (SelectList)ViewData["SelectList"], new { @class = "inputClass" })
<div id="seasonEditorSection"> <div id="stylized" style="border-color: transparent;">
<div style="font-weight: bold; padding-right: 15px; padding-bottom: 5px;"> <div class="settingsForm clearfix">
@Html.LabelFor(m => m.SeasonEditor) @Html.HiddenFor(m => m.SeriesId)
<span id="seasonEditorLoader"> <label class="labelClass">@Html.LabelFor(m => m.Monitored)
<img src="../../../Content/Images/ajax-loader.gif" width="14px" height="14px" style="margin-bottom: -2px;" /></span> <span class="small">@Html.DescriptionFor(m => m.Monitored)</span>
</div> </label>
<div id="season-editor"> @Html.CheckBoxFor(m => m.Monitored, new { @class = "inputClass checkClass" })
</div> <label class="labelClass">@Html.LabelFor(m => m.SeasonFolder)
<span class="small">@Html.DescriptionFor(m => m.SeasonFolder)</span>
</label>
@Html.CheckBoxFor(m => m.SeasonFolder, new { @class = "inputClass checkClass" })
<label class="labelClass">@Html.LabelFor(m => m.QualityProfileId)
<span class="small">@Html.DescriptionFor(m => m.QualityProfileId)</span>
</label>
@Html.DropDownListFor(m => m.QualityProfileId, (SelectList)ViewData["SelectList"], new { @class = "inputClass" })
<label class="labelClass">@Html.LabelFor(m => m.Path)
<span class="small">@Html.DescriptionFor(m => m.Path)</span>
</label>
@Html.TextBoxFor(m => m.Path, new { @class = "inputClass" })
</div> </div>
</div> </div>
<span id="ajaxSaveWheel" style="display: none; float: right; padding-right: 368px; <span id="ajaxSaveWheel" style="display: none; float: right; padding-right: 550px;
padding-top: 1.5px;"> padding-top: 1.5px;">
<img src="../../../Content/Images/ajax-loader.gif" width="20px" height="20px" /></span> <img src="../../../Content/Images/ajax-loader.gif" width="20px" height="20px" /></span>

@ -1,9 +1,11 @@
@using NzbDrone.Core.Repository; @using NzbDrone.Core.Repository;
@using NzbDrone.Web.Controllers
@using NzbDrone.Web.Models; @using NzbDrone.Web.Models;
@model IEnumerable<NzbDrone.Core.Repository.Series> @model IEnumerable<NzbDrone.Core.Repository.Series>
@section TitleContent{ @section TitleContent{
NZBDrone NZBDrone
} }
<style> <style>
/* progress bar container */ /* progress bar container */
.progressbar .progressbar
@ -46,8 +48,12 @@ NZBDrone
background: #E5ECF9; background: #E5ECF9;
} }
</style> </style>
@section ActionMenu{ @section ActionMenu{
@{Html.RenderPartial("SubMenu");} <ul id="sub-menu">
<li>@Html.ActionLink("Add Series", "Index", "AddSeries")</li>
<li>@Ajax.ActionLink("Start RSS Sync", "RssSync", "Command", null)</li>
</ul>
} }
@section MainContent{ @section MainContent{
<div class="grid-container"> <div class="grid-container">
@ -103,37 +109,10 @@ NZBDrone
.closest(".t-window") .closest(".t-window")
.data("tWindow") .data("tWindow")
.center(); .center();
var seriesId = args.dataItem.SeriesId;
var url = '@Url.Action("SeasonEditor", "Series")';
$('#season-editor').load(url, { seriesId: seriesId }, function (response, status, xhr) {
$('#seasonEditorLoader').hide();
});
} }
function grid_save(e) { function grid_save(e) {
$('#ajaxSaveWheel').show(); $('#ajaxSaveWheel').show();
var seasonEditor = e.form.SeasonEditor_collection;
var saveSeasonEditUrl = '@Url.Action("SaveSeason", "Series")';
jQuery.each(seasonEditor, function () {
var guid = $(this).val();
var prefix = '#SeasonEditor_' + guid + '__';
var seriesId = $(prefix + 'SeriesId').val();
var seasonNumber = $(prefix + 'SeasonNumber').val();
var monitored = $(prefix + 'Monitored').attr('checked');
$.ajax({
type: "POST",
url: saveSeasonEditUrl,
data: jQuery.param({ seriesId: seriesId, seasonNumber: seasonNumber, monitored: monitored }),
error: function (req, status, error) {
alert("Sorry! We could save season changes at this time. " + error);
},
success: function (data, textStatus, jqXHR) { }
});
});
} }
function grid_rowBound(e) { function grid_rowBound(e) {
@ -144,8 +123,7 @@ NZBDrone
$("#progressbar_" + seriesId).episodeProgress(episodeFileCount, episodeCount); $("#progressbar_" + seriesId).episodeProgress(episodeFileCount, episodeCount);
} }
</script>
<script type="text/javascript">
(function ($) { (function ($) {
$.fn.episodeProgress = function (episodes, totalEpisodes) { $.fn.episodeProgress = function (episodes, totalEpisodes) {
return this.each( return this.each(

@ -1,36 +0,0 @@
@using NzbDrone.Web.Models;
@model List<SeasonEditModel>
@{
Layout = null;
}
<div style="vertical-align: middle">
@foreach (var season in Model)
{
Html.RenderAction("GetSingleSeasonView", "Series", season);
}
</div>
@section Scripts{
<script type="text/javascript">
var lastChecked = null;
$(document).ready(function () {
$('.chkbox').click(function (event) {
if (!lastChecked) {
lastChecked = this;
return;
}
if (event.shiftKey) {
var start = $('.chkbox').index(this);
var end = $('.chkbox').index(lastChecked);
for (i = Math.min(start, end); i <= Math.max(start, end); i++) {
$('.chkbox')[i].checked = lastChecked.checked;
}
}
lastChecked = this;
});
});
</script>
}

@ -1,7 +0,0 @@
@using NzbDrone.Web.Controllers
@{Html.Telerik().Menu().Name("telerikGrid").Items(items =>
{
items.Add().Text("Add Series").Action<AddSeriesController>(c => c.Index());
items.Add().Text("Start RSS Sync").Action<SeriesController>(c => c.RssSync());
}).Render();}

@ -81,7 +81,7 @@
</div> </div>
</div> </div>
<button type="submit" id="save_button" >Save</button><img src="../../Content/Images/ajax-loader.gif" alt="Loader" id="saveAjax"/> <button type="submit" id="save_button" disabled="disabled">Save</button><img src="../../Content/Images/ajax-loader.gif" alt="Loader" id="saveAjax"/>
} }
</div> </div>
<div id="result" class="hiddenResult"></div> <div id="result" class="hiddenResult"></div>

@ -9,8 +9,7 @@
{ {
padding-top: 20px; padding-top: 20px;
} }
</style>
</style>
} }
@section TitleContent{ @section TitleContent{
@ -26,7 +25,7 @@
@using (Html.BeginForm("SaveIndexers", "Settings", FormMethod.Post, new { id = "form", name = "form", @class = "settingsForm" })) @using (Html.BeginForm("SaveIndexers", "Settings", FormMethod.Post, new { id = "form", name = "form", @class = "settingsForm" }))
{ {
<h1>Indexer</h1> <h1>Indexers</h1>
<p></p> <p></p>
@Html.ValidationSummary(true, "Unable to save your settings. Please correct the errors and try again.") @Html.ValidationSummary(true, "Unable to save your settings. Please correct the errors and try again.")
@ -126,10 +125,14 @@
} }
</div> </div>
<br/> <br/>
<button type="submit" id="save_button" >Save</button><img src="../../Content/Images/ajax-loader.gif" alt="Loader" id="saveAjax"/> <button type="submit" id="save_button" disabled="disabled">Save</button><img src="../../Content/Images/ajax-loader.gif" alt="Loader" id="saveAjax"/>
} }
</div> </div>
<div id="result" class="hiddenResult"></div> <div id="result" class="hiddenResult"></div>
} }
@section Scripts{
<script src="/Scripts/settingsForm.js" type="text/javascript"></script>
}

@ -92,7 +92,7 @@
@Html.TextBoxFor(m => m.XbmcPassword, new { @class = "inputClass" }) @Html.TextBoxFor(m => m.XbmcPassword, new { @class = "inputClass" })
</div> </div>
<button type="submit" id="save_button" >Save</button><img src="../../Content/Images/ajax-loader.gif" alt="Loader" id="saveAjax"/> <button type="submit" id="save_button" disabled="disabled">Save</button><img src="../../Content/Images/ajax-loader.gif" alt="Loader" id="saveAjax"/>
} }
</div> </div>

@ -40,7 +40,7 @@ Settings
</div> </div>
</div> </div>
<br /> <br />
<button type="submit" id="save_button" >Save</button><img src="../../Content/Images/ajax-loader.gif" alt="Loader" id="saveAjax"/> <button type="submit" id="save_button" disabled="disabled">Save</button><img src="../../Content/Images/ajax-loader.gif" alt="Loader" id="saveAjax"/>
</div> </div>
} }
</div> </div>

@ -66,7 +66,7 @@
</label> </label>
@Html.TextBoxFor(m => m.SabDropDirectory, new { @class = "inputClass folderLookup" }) @Html.TextBoxFor(m => m.SabDropDirectory, new { @class = "inputClass folderLookup" })
<button type="submit" id="save_button" >Save</button><img src="../../Content/Images/ajax-loader.gif" alt="Loader" id="saveAjax"/> <button type="submit" id="save_button" disabled="disabled">Save</button><img src="../../Content/Images/ajax-loader.gif" alt="Loader" id="saveAjax"/>
} }
</div> </div>

@ -1,13 +1,9 @@
@{Html.Telerik().Menu().Name("SubMenu").Items(items => <ul id="sub-menu">
{ <li>@Html.ActionLink("Indexers", "Indexers", "Settings")</li>
items.Add().Text("Indexers").Action("Indexers", "Settings"); <li>@Html.ActionLink("SABnzbd", "Sabnzbd", "Settings")</li>
items.Add().Text("SABnzbd").Action("Sabnzbd", "Settings"); <li>@Html.ActionLink("Quality", "Quality", "Settings")</li>
items.Add().Text("Quality").Action("Quality", "Settings"); <li>@Html.ActionLink("Episode Sorting", "EpisodeSorting", "Settings")</li>
items.Add().Text("Episode Sorting").Action("EpisodeSorting", <li>@Html.ActionLink("Notifications", "Notifications", "Settings")</li>
"Settings"); </ul>
items.Add().Text("Notifications").Action("Notifications",
"Settings");
}).Render();
}
<div style="margin-bottom: 10px"></div> <div style="margin-bottom: 10px"></div>

@ -1,94 +1 @@
<style> 
/*body{
font-family:"Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif;
font-size:12px;
}
p, h1, form, button{border:0; margin:0; padding:0;}*/
.spacer{clear:both; height:1px;}
/* ----------- My Form ----------- */
.myform{
margin:0 auto;
width:400px;
padding:14px;
}
/* ----------- stylized ----------- */
#stylized{
border:solid 2px #b7ddf2;
background:#ebf4fb;
}
#stylized h1 {
font-size:14px;
font-weight:bold;
margin-bottom:8px;
}
#stylized p{
font-size:11px;
color:#666666;
margin-bottom:20px;
border-bottom:solid 1px #b7ddf2;
padding-bottom:10px;
}
#stylized label{
display:block;
font-weight:bold;
text-align:right;
width:140px;
float:left;
}
#stylized .small{
color:#666666;
display:block;
font-size:11px;
font-weight:normal;
text-align:right;
width:140px;
}
#stylized input{
float:left;
font-size:12px;
padding:4px 2px;
border:solid 1px #aacfe4;
width:200px;
margin:2px 0 20px 10px;
}
#stylized button{
clear:both;
margin-left:150px;
width:125px;
height:31px;
background:#666666 url(img/button.png) no-repeat;
text-align:center;
line-height:31px;
color:#FFFFFF;
font-size:11px;
font-weight:bold;
}
</style>
<div id="stylized" class="myform">
<form id="form" name="form" method="post">
<fieldset>
<h1>Sign-up form</h1>
<p>This is the basic look of my form without table</p>
<label>Name
<span class="small">Add your name</span>
</label>
<input type="text" name="name" id="name" />
<label>Email
<span class="small">Add a valid address</span>
</label>
<input type="text" name="email" id="email" />
<label>Password
<span class="small">Min. size 6 chars</span>
</label>
<input type="text" name="password" id="password" />
<button type="submit">Sign-up</button>
<div class="spacer"></div>
</fieldset>
</form>
</div>

@ -13,6 +13,7 @@
<link type="text/css" rel="stylesheet" href="/Content/Notibar.css" /> <link type="text/css" rel="stylesheet" href="/Content/Notibar.css" />
<link type="text/css" rel="stylesheet" href="/Content/ActionButton.css" /> <link type="text/css" rel="stylesheet" href="/Content/ActionButton.css" />
<link type="text/css" rel="stylesheet" href="/Content/overrides.css" /> <link type="text/css" rel="stylesheet" href="/Content/overrides.css" />
<link type="text/css" rel="stylesheet" href="/Content/Menu.css" />
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.14/jquery-ui.min.js"></script> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.14/jquery-ui.min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/yui/3.3.0/build/yui/yui-min.js"></script> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/yui/3.3.0/build/yui/yui-min.js"></script>

@ -4,8 +4,9 @@
Upcoming Upcoming
} }
@section ActionMenu{ @section ActionMenu{
@{Html.Telerik().Menu().Name("historyMenu").Items( <ul id="sub-menu">
items => { items.Add().Text("Start RSS Sync").Action("RssSync", "Series"); }).Render();} <li>@Ajax.ActionLink("Start RSS Sync", "RssSync", "Command", null, null)</li>
</ul>
} }
@section MainContent{ @section MainContent{
<div id="yesterday"> <div id="yesterday">

Loading…
Cancel
Save