mirror of
https://github.com/Sonarr/Sonarr.git
synced 2024-10-30 23:42:33 +01:00
Merge branch 'season-start' into develop
This commit is contained in:
commit
4025d6ddee
@ -140,8 +140,6 @@
|
||||
<Compile Include="RootFolders\RootFolderModule.cs" />
|
||||
<Compile Include="RootFolders\RootFolderResource.cs" />
|
||||
<Compile Include="RootFolders\RootFolderConnection.cs" />
|
||||
<Compile Include="Seasons\SeasonModule.cs" />
|
||||
<Compile Include="Seasons\SeasonResource.cs" />
|
||||
<Compile Include="Series\SeriesConnection.cs" />
|
||||
<Compile Include="Series\SeriesResource.cs" />
|
||||
<Compile Include="Series\SeriesModule.cs" />
|
||||
|
@ -1,53 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Api.Mapping;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Api.Seasons
|
||||
{
|
||||
public class SeasonModule : NzbDroneRestModule<SeasonResource>
|
||||
{
|
||||
private readonly ISeasonService _seasonService;
|
||||
|
||||
public SeasonModule(ISeasonService seasonService)
|
||||
: base("/season")
|
||||
{
|
||||
_seasonService = seasonService;
|
||||
|
||||
GetResourceAll = GetSeasons;
|
||||
GetResourceById = GetSeason;
|
||||
UpdateResource = Update;
|
||||
|
||||
Post["/pass"] = x => SetSeasonPass();
|
||||
}
|
||||
|
||||
private List<SeasonResource> GetSeasons()
|
||||
{
|
||||
var seriesId = Request.Query.SeriesId;
|
||||
|
||||
if (seriesId.HasValue)
|
||||
{
|
||||
return ToListResource<Season>(() => _seasonService.GetSeasonsBySeries(seriesId));
|
||||
}
|
||||
|
||||
return ToListResource(() => _seasonService.GetAllSeasons());
|
||||
}
|
||||
|
||||
private SeasonResource GetSeason(int id)
|
||||
{
|
||||
return _seasonService.Get(id).InjectTo<SeasonResource>();
|
||||
}
|
||||
|
||||
private void Update(SeasonResource seasonResource)
|
||||
{
|
||||
_seasonService.SetMonitored(seasonResource.SeriesId, seasonResource.SeasonNumber, seasonResource.Monitored);
|
||||
}
|
||||
|
||||
private List<SeasonResource> SetSeasonPass()
|
||||
{
|
||||
var seriesId = Request.Form.SeriesId;
|
||||
var seasonNumber = Request.Form.SeasonNumber;
|
||||
|
||||
return ToListResource<Season>(() => _seasonService.SetSeasonPass(seriesId, seasonNumber));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
using System;
|
||||
using NzbDrone.Api.REST;
|
||||
|
||||
namespace NzbDrone.Api.Seasons
|
||||
{
|
||||
public class SeasonResource : RestResource
|
||||
{
|
||||
public int SeriesId { get; set; }
|
||||
public int SeasonNumber { get; set; }
|
||||
public Boolean Monitored { get; set; }
|
||||
}
|
||||
}
|
@ -117,7 +117,6 @@ namespace NzbDrone.Api.Series
|
||||
{
|
||||
resource.EpisodeCount = seriesStatistics.EpisodeCount;
|
||||
resource.EpisodeFileCount = seriesStatistics.EpisodeFileCount;
|
||||
resource.SeasonCount = seriesStatistics.SeasonCount;
|
||||
resource.NextAiring = seriesStatistics.NextAiring;
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,17 @@ namespace NzbDrone.Api.Series
|
||||
|
||||
//View Only
|
||||
public String Title { get; set; }
|
||||
public Int32 SeasonCount { get; set; }
|
||||
|
||||
public Int32 SeasonCount
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Seasons != null) return Seasons.Count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public Int32 EpisodeCount { get; set; }
|
||||
public Int32 EpisodeFileCount { get; set; }
|
||||
public SeriesStatusType Status { get; set; }
|
||||
@ -26,7 +36,8 @@ namespace NzbDrone.Api.Series
|
||||
public List<MediaCover> Images { get; set; }
|
||||
|
||||
public String RemotePoster { get; set; }
|
||||
|
||||
public List<Season> Seasons { get; set; }
|
||||
public Int32 Year { get; set; }
|
||||
|
||||
//View & Edit
|
||||
public String Path { get; set; }
|
||||
|
@ -177,7 +177,6 @@
|
||||
<Compile Include="TvTests\EpisodeRepositoryTests\EpisodesRepositoryReadFixture.cs" />
|
||||
<Compile Include="TvTests\EpisodeRepositoryTests\EpisodesWithoutFilesFixture.cs" />
|
||||
<Compile Include="TvTests\EpisodeRepositoryTests\EpisodesBetweenDatesFixture.cs" />
|
||||
<Compile Include="TvTests\SeasonProviderTest.cs" />
|
||||
<Compile Include="DecisionEngineTests\RetentionSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\QualityAllowedByProfileSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\UpgradeHistorySpecificationFixture.cs" />
|
||||
@ -188,9 +187,6 @@
|
||||
<Compile Include="ProviderTests\DiskProviderTests\ArchiveProviderFixture.cs" />
|
||||
<Compile Include="MediaFiles\DownloadedEpisodesImportServiceFixture.cs" />
|
||||
<Compile Include="SeriesStatsTests\SeriesStatisticsFixture.cs" />
|
||||
<Compile Include="TvTests\SeasonServiceTests\HandleEpisodeInfoDeletedEventFixture.cs" />
|
||||
<Compile Include="TvTests\SeasonServiceTests\SetSeasonPassFixture.cs" />
|
||||
<Compile Include="TvTests\SeasonServiceTests\SetMonitoredFixture.cs" />
|
||||
<Compile Include="TvTests\SeriesRepositoryTests\QualityProfileRepositoryFixture.cs" />
|
||||
<Compile Include="UpdateTests\UpdateServiceFixture.cs" />
|
||||
<Compile Include="ProviderTests\XemCommunicationProviderTests\GetSceneTvdbMappingsFixture.cs" />
|
||||
|
@ -1,57 +0,0 @@
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.TvTests
|
||||
{
|
||||
public class SeasonProviderTest : DbTest<SeasonRepository, Season>
|
||||
{
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public void Ismonitored_should_return_monitored_status_of_season(bool monitored)
|
||||
{
|
||||
var fakeSeason = Builder<Season>.CreateNew()
|
||||
.With(s => s.Monitored = monitored)
|
||||
.BuildNew<Season>();
|
||||
|
||||
Db.Insert(fakeSeason);
|
||||
|
||||
var result = Subject.IsMonitored(fakeSeason.SeriesId, fakeSeason.SeasonNumber);
|
||||
|
||||
result.Should().Be(monitored);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Monitored_should_return_true_if_not_in_db()
|
||||
{
|
||||
Subject.IsMonitored(10, 0).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetSeason_should_return_seasons_for_specified_series_only()
|
||||
{
|
||||
var seriesA = new[] { 1, 2, 3 };
|
||||
var seriesB = new[] { 4, 5, 6 };
|
||||
|
||||
var seasonsA = seriesA.Select(c => new Season {SeasonNumber = c, SeriesId = 1}).ToList();
|
||||
var seasonsB = seriesB.Select(c => new Season {SeasonNumber = c, SeriesId = 2}).ToList();
|
||||
|
||||
Subject.InsertMany(seasonsA);
|
||||
Subject.InsertMany(seasonsB);
|
||||
|
||||
Subject.GetSeasonNumbers(1).Should().Equal(seriesA);
|
||||
Subject.GetSeasonNumbers(2).Should().Equal(seriesB);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void GetSeason_should_return_emptylist_if_series_doesnt_exist()
|
||||
{
|
||||
Subject.GetSeasonNumbers(1).Should().BeEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using FizzWare.NBuilder;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Tv.Events;
|
||||
|
||||
namespace NzbDrone.Core.Test.TvTests.SeasonServiceTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class HandleEpisodeInfoDeletedEventFixture : CoreTest<SeasonService>
|
||||
{
|
||||
private List<Season> _seasons;
|
||||
private List<Episode> _episodes;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_seasons = Builder<Season>
|
||||
.CreateListOfSize(1)
|
||||
.All()
|
||||
.With(s => s.SeriesId = 1)
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
_episodes = Builder<Episode>
|
||||
.CreateListOfSize(1)
|
||||
.All()
|
||||
.With(e => e.SeasonNumber = _seasons.First().SeasonNumber)
|
||||
.With(s => s.SeriesId = _seasons.First().SeasonNumber)
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
Mocker.GetMock<ISeasonRepository>()
|
||||
.Setup(s => s.GetSeasonBySeries(It.IsAny<int>()))
|
||||
.Returns(_seasons);
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
.Setup(s => s.GetEpisodesBySeason(It.IsAny<int>(), _seasons.First().SeasonNumber))
|
||||
.Returns(_episodes);
|
||||
}
|
||||
|
||||
private void GivenAbandonedSeason()
|
||||
{
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
.Setup(s => s.GetEpisodesBySeason(It.IsAny<int>(), _seasons.First().SeasonNumber))
|
||||
.Returns(new List<Episode>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_delete_when_season_is_still_valid()
|
||||
{
|
||||
Subject.Handle(new EpisodeInfoDeletedEvent(_episodes));
|
||||
|
||||
Mocker.GetMock<ISeasonRepository>()
|
||||
.Verify(v => v.Delete(It.IsAny<Season>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_delete_season_if_no_episodes_exist_in_that_season()
|
||||
{
|
||||
GivenAbandonedSeason();
|
||||
|
||||
Subject.Handle(new EpisodeInfoDeletedEvent(_episodes));
|
||||
|
||||
Mocker.GetMock<ISeasonRepository>()
|
||||
.Verify(v => v.Delete(It.IsAny<Season>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_only_delete_a_season_once()
|
||||
{
|
||||
_episodes = Builder<Episode>
|
||||
.CreateListOfSize(5)
|
||||
.All()
|
||||
.With(e => e.SeasonNumber = _seasons.First().SeasonNumber)
|
||||
.With(s => s.SeriesId = _seasons.First().SeasonNumber)
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
GivenAbandonedSeason();
|
||||
|
||||
Subject.Handle(new EpisodeInfoDeletedEvent(_episodes));
|
||||
|
||||
Mocker.GetMock<ISeasonRepository>()
|
||||
.Verify(v => v.Delete(It.IsAny<Season>()), Times.Once());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.Test.TvTests.SeasonServiceTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class SetMonitoredFixture : CoreTest<SeasonService>
|
||||
{
|
||||
private Season _season;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_season = new Season
|
||||
{
|
||||
Id = 1,
|
||||
SeasonNumber = 1,
|
||||
SeriesId = 1,
|
||||
Monitored = false
|
||||
};
|
||||
|
||||
Mocker.GetMock<ISeasonRepository>()
|
||||
.Setup(s => s.Get(It.IsAny<Int32>(), It.IsAny<Int32>()))
|
||||
.Returns(_season);
|
||||
}
|
||||
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public void should_update_season(bool monitored)
|
||||
{
|
||||
Subject.SetMonitored(_season.SeriesId, _season.SeasonNumber, monitored);
|
||||
|
||||
Mocker.GetMock<ISeasonRepository>()
|
||||
.Verify(v => v.Update(_season), Times.Once());
|
||||
}
|
||||
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public void should_update_episodes_in_season(bool monitored)
|
||||
{
|
||||
Subject.SetMonitored(_season.SeriesId, _season.SeasonNumber, monitored);
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
.Verify(v => v.SetEpisodeMonitoredBySeason(_season.SeriesId, _season.SeasonNumber, monitored), Times.Once());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,91 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.Test.TvTests.SeasonServiceTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class SetSeasonPassFixture : CoreTest<SeasonService>
|
||||
{
|
||||
private const Int32 SERIES_ID = 1;
|
||||
private List<Season> _seasons;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_seasons = Builder<Season>.CreateListOfSize(5)
|
||||
.All()
|
||||
.With(s => s.SeriesId = SERIES_ID)
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
Mocker.GetMock<ISeasonRepository>()
|
||||
.Setup(s => s.GetSeasonBySeries(It.IsAny<Int32>()))
|
||||
.Returns(_seasons);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_updateMany()
|
||||
{
|
||||
Subject.SetSeasonPass(SERIES_ID, 1);
|
||||
|
||||
Mocker.GetMock<ISeasonRepository>()
|
||||
.Verify(v => v.UpdateMany(It.IsAny<List<Season>>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_set_lower_seasons_to_false()
|
||||
{
|
||||
const int seasonNumber = 3;
|
||||
|
||||
var result = Subject.SetSeasonPass(SERIES_ID, seasonNumber);
|
||||
|
||||
result.Where(s => s.SeasonNumber < seasonNumber).Should().OnlyContain(s => s.Monitored == false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_set_equal_or_higher_seasons_to_false()
|
||||
{
|
||||
const int seasonNumber = 3;
|
||||
|
||||
var result = Subject.SetSeasonPass(SERIES_ID, seasonNumber);
|
||||
|
||||
result.Where(s => s.SeasonNumber >= seasonNumber).Should().OnlyContain(s => s.Monitored == true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_set_episodes_in_lower_seasons_to_false()
|
||||
{
|
||||
const int seasonNumber = 3;
|
||||
|
||||
Subject.SetSeasonPass(SERIES_ID, seasonNumber);
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
.Verify(v => v.SetEpisodeMonitoredBySeason(SERIES_ID, It.Is<Int32>(i => i < seasonNumber), false), Times.AtLeastOnce());
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
.Verify(v => v.SetEpisodeMonitoredBySeason(SERIES_ID, It.Is<Int32>(i => i < seasonNumber), true), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_set_episodes_in_equal_or_higher_seasons_to_false()
|
||||
{
|
||||
const int seasonNumber = 3;
|
||||
|
||||
Subject.SetSeasonPass(SERIES_ID, seasonNumber);
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
.Verify(v => v.SetEpisodeMonitoredBySeason(SERIES_ID, It.Is<Int32>(i => i >= seasonNumber), true), Times.AtLeastOnce());
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
.Verify(v => v.SetEpisodeMonitoredBySeason(SERIES_ID, It.Is<Int32>(i => i >= seasonNumber), false), Times.Never());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(20)]
|
||||
public class add_year_and_seasons_to_series : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Alter.Table("Series").AddColumn("Year").AsInt32().Nullable();
|
||||
Alter.Table("Series").AddColumn("Seasons").AsString().Nullable();
|
||||
|
||||
Execute.WithConnection(ConvertSeasons);
|
||||
}
|
||||
|
||||
private void ConvertSeasons(IDbConnection conn, IDbTransaction tran)
|
||||
{
|
||||
using (IDbCommand allSeriesCmd = conn.CreateCommand())
|
||||
{
|
||||
allSeriesCmd.Transaction = tran;
|
||||
allSeriesCmd.CommandText = @"SELECT Id FROM Series";
|
||||
using (IDataReader allSeriesReader = allSeriesCmd.ExecuteReader())
|
||||
{
|
||||
while (allSeriesReader.Read())
|
||||
{
|
||||
int seriesId = allSeriesReader.GetInt32(0);
|
||||
var seasons = new List<dynamic>();
|
||||
|
||||
using (IDbCommand seasonsCmd = conn.CreateCommand())
|
||||
{
|
||||
seasonsCmd.Transaction = tran;
|
||||
seasonsCmd.CommandText = String.Format(@"SELECT SeasonNumber, Monitored FROM Seasons WHERE SeriesId = {0}", seriesId);
|
||||
|
||||
using (IDataReader seasonReader = seasonsCmd.ExecuteReader())
|
||||
{
|
||||
while (seasonReader.Read())
|
||||
{
|
||||
int seasonNumber = seasonReader.GetInt32(0);
|
||||
bool monitored = seasonReader.GetBoolean(1);
|
||||
|
||||
seasons.Add(new { seasonNumber, monitored });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using (IDbCommand updateCmd = conn.CreateCommand())
|
||||
{
|
||||
var text = String.Format("UPDATE Series SET Seasons = '{0}' WHERE Id = {1}", seasons.ToJson() , seriesId);
|
||||
|
||||
updateCmd.Transaction = tran;
|
||||
updateCmd.CommandText = text;
|
||||
updateCmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
14
NzbDrone.Core/Datastore/Migration/021_drop_seasons_table.cs
Normal file
14
NzbDrone.Core/Datastore/Migration/021_drop_seasons_table.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(21)]
|
||||
public class drop_seasons_table : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Delete.Table("Seasons");
|
||||
}
|
||||
}
|
||||
}
|
@ -45,8 +45,6 @@ namespace NzbDrone.Core.Datastore
|
||||
.Relationship()
|
||||
.HasOne(s => s.QualityProfile, s => s.QualityProfileId);
|
||||
|
||||
Mapper.Entity<Season>().RegisterModel("Seasons");
|
||||
|
||||
Mapper.Entity<Episode>().RegisterModel("Episodes")
|
||||
.Ignore(e => e.SeriesTitle)
|
||||
.Ignore(e => e.Series)
|
||||
|
@ -29,31 +29,4 @@ namespace NzbDrone.Core.MetadataSource.Trakt
|
||||
public List<string> genres { get; set; }
|
||||
public List<Season> seasons { get; set; }
|
||||
}
|
||||
|
||||
public class SearchShow
|
||||
{
|
||||
public string title { get; set; }
|
||||
public int year { get; set; }
|
||||
public string url { get; set; }
|
||||
public int first_aired { get; set; }
|
||||
public string first_aired_iso { get; set; }
|
||||
public int first_aired_utc { get; set; }
|
||||
public string country { get; set; }
|
||||
public string overview { get; set; }
|
||||
public int runtime { get; set; }
|
||||
public string status { get; set; }
|
||||
public string network { get; set; }
|
||||
public string air_day { get; set; }
|
||||
public string air_day_utc { get; set; }
|
||||
public string air_time { get; set; }
|
||||
public string air_time_utc { get; set; }
|
||||
public string certification { get; set; }
|
||||
public string imdb_id { get; set; }
|
||||
public int tvdb_id { get; set; }
|
||||
public int tvrage_id { get; set; }
|
||||
public int last_updated { get; set; }
|
||||
public string poster { get; set; }
|
||||
public Images images { get; set; }
|
||||
public List<string> genres { get; set; }
|
||||
}
|
||||
}
|
@ -30,10 +30,10 @@ namespace NzbDrone.Core.MetadataSource
|
||||
try
|
||||
{
|
||||
var client = BuildClient("search", "shows");
|
||||
var restRequest = new RestRequest(GetSearchTerm(title));
|
||||
var response = client.ExecuteAndValidate<List<SearchShow>>(restRequest);
|
||||
var restRequest = new RestRequest(GetSearchTerm(title) +"/30/seasons");
|
||||
var response = client.ExecuteAndValidate<List<Show>>(restRequest);
|
||||
|
||||
return response.Select(MapSearchSeries).ToList();
|
||||
return response.Select(MapSeries).ToList();
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
@ -71,6 +71,7 @@ namespace NzbDrone.Core.MetadataSource
|
||||
series.ImdbId = show.imdb_id;
|
||||
series.Title = show.title;
|
||||
series.CleanTitle = Parser.Parser.CleanSeriesTitle(show.title);
|
||||
series.Year = show.year;
|
||||
series.FirstAired = FromIso(show.first_aired_iso);
|
||||
series.Overview = show.overview;
|
||||
series.Runtime = show.runtime;
|
||||
@ -79,27 +80,10 @@ namespace NzbDrone.Core.MetadataSource
|
||||
series.TitleSlug = show.url.ToLower().Replace("http://trakt.tv/show/", "");
|
||||
series.Status = GetSeriesStatus(show.status);
|
||||
|
||||
series.Images.Add(new MediaCover.MediaCover { CoverType = MediaCoverTypes.Banner, Url = show.images.banner });
|
||||
series.Images.Add(new MediaCover.MediaCover { CoverType = MediaCoverTypes.Poster, Url = GetPosterThumbnailUrl(show.images.poster) });
|
||||
series.Images.Add(new MediaCover.MediaCover { CoverType = MediaCoverTypes.Fanart, Url = show.images.fanart });
|
||||
return series;
|
||||
}
|
||||
|
||||
private static Series MapSearchSeries(SearchShow show)
|
||||
{
|
||||
var series = new Series();
|
||||
series.TvdbId = show.tvdb_id;
|
||||
series.TvRageId = show.tvrage_id;
|
||||
series.ImdbId = show.imdb_id;
|
||||
series.Title = show.title;
|
||||
series.CleanTitle = Parser.Parser.CleanSeriesTitle(show.title);
|
||||
series.FirstAired = FromIso(show.first_aired_iso);
|
||||
series.Overview = show.overview;
|
||||
series.Runtime = show.runtime;
|
||||
series.Network = show.network;
|
||||
series.AirTime = show.air_time_utc;
|
||||
series.TitleSlug = show.url.ToLower().Replace("http://trakt.tv/show/", "");
|
||||
series.Status = GetSeriesStatus(show.status);
|
||||
series.Seasons = show.seasons.Select(s => new Tv.Season
|
||||
{
|
||||
SeasonNumber = s.season
|
||||
}).OrderByDescending(s => s.SeasonNumber).ToList();
|
||||
|
||||
series.Images.Add(new MediaCover.MediaCover { CoverType = MediaCoverTypes.Banner, Url = show.images.banner });
|
||||
series.Images.Add(new MediaCover.MediaCover { CoverType = MediaCoverTypes.Poster, Url = GetPosterThumbnailUrl(show.images.poster) });
|
||||
|
@ -163,6 +163,8 @@
|
||||
<Compile Include="Datastore\Migration\017_reset_scene_names.cs" />
|
||||
<Compile Include="Datastore\Migration\018_remove_duplicates.cs" />
|
||||
<Compile Include="Datastore\Migration\019_restore_unique_constraints.cs" />
|
||||
<Compile Include="Datastore\Migration\020_add_year_and_seasons_to_series.cs" />
|
||||
<Compile Include="Datastore\Migration\021_drop_seasons_table.cs" />
|
||||
<Compile Include="Datastore\Migration\Framework\MigrationContext.cs" />
|
||||
<Compile Include="Datastore\Migration\Framework\MigrationController.cs" />
|
||||
<Compile Include="Datastore\Migration\Framework\MigrationExtension.cs" />
|
||||
|
@ -6,7 +6,6 @@ namespace NzbDrone.Core.SeriesStats
|
||||
public class SeriesStatistics : ResultSet
|
||||
{
|
||||
public int SeriesId { get; set; }
|
||||
public int SeasonCount { get; set; }
|
||||
public string NextAiringString { get; set; }
|
||||
public int EpisodeFileCount { get; set; }
|
||||
public int EpisodeCount { get; set; }
|
||||
|
@ -56,7 +56,6 @@ namespace NzbDrone.Core.SeriesStats
|
||||
SeriesId,
|
||||
SUM(CASE WHEN (Monitored = 1 AND AirdateUtc <= @currentDate) OR EpisodeFileId > 0 THEN 1 ELSE 0 END) AS EpisodeCount,
|
||||
SUM(CASE WHEN EpisodeFileId > 0 THEN 1 ELSE 0 END) AS EpisodeFileCount,
|
||||
COUNT(DISTINCT(CASE WHEN SeasonNumber > 0 THEN SeasonNumber ELSE NULL END)) as SeasonCount,
|
||||
MIN(CASE WHEN AirDateUtc < @currentDate OR EpisodeFileId > 0 THEN NULL ELSE AirDateUtc END) AS NextAiringString
|
||||
FROM Episodes";
|
||||
}
|
||||
|
@ -114,11 +114,6 @@ namespace NzbDrone.Core.Tv
|
||||
|
||||
private static bool GetMonitoredStatus(Episode episode, IEnumerable<Season> seasons)
|
||||
{
|
||||
if (episode.SeasonNumber == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (episode.EpisodeNumber == 0 && episode.SeasonNumber != 1)
|
||||
{
|
||||
return false;
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
@ -64,6 +65,8 @@ namespace NzbDrone.Core.Tv
|
||||
_logger.WarnException("Couldn't update series path for " + series.Path, e);
|
||||
}
|
||||
|
||||
series.Seasons = UpdateSeasons(series, seriesInfo);
|
||||
|
||||
_seriesService.UpdateSeries(series);
|
||||
_refreshEpisodeService.RefreshEpisodeInfo(series, tuple.Item2);
|
||||
|
||||
@ -71,6 +74,21 @@ namespace NzbDrone.Core.Tv
|
||||
_messageAggregator.PublishEvent(new SeriesUpdatedEvent(series));
|
||||
}
|
||||
|
||||
private List<Season> UpdateSeasons(Series series, Series seriesInfo)
|
||||
{
|
||||
foreach (var season in seriesInfo.Seasons)
|
||||
{
|
||||
var existingSeason = series.Seasons.SingleOrDefault(s => s.SeasonNumber == season.SeasonNumber);
|
||||
|
||||
if (existingSeason != null)
|
||||
{
|
||||
season.Monitored = existingSeason.Monitored;
|
||||
}
|
||||
}
|
||||
|
||||
return seriesInfo.Seasons;
|
||||
}
|
||||
|
||||
public void Execute(RefreshSeriesCommand message)
|
||||
{
|
||||
if (message.SeriesId.HasValue)
|
||||
|
@ -4,12 +4,9 @@ using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Tv
|
||||
{
|
||||
public class Season : ModelBase
|
||||
public class Season : IEmbeddedDocument
|
||||
{
|
||||
public int SeriesId { get; set; }
|
||||
public int SeasonNumber { get; set; }
|
||||
public Boolean Monitored { get; set; }
|
||||
|
||||
public List<Episode> Episodes { get; set; }
|
||||
}
|
||||
}
|
@ -6,44 +6,21 @@ using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Tv
|
||||
{
|
||||
public interface ISeasonRepository : IBasicRepository<Season>
|
||||
public interface ISeasonRepository : IBasicRepository<Series>
|
||||
{
|
||||
IList<int> GetSeasonNumbers(int seriesId);
|
||||
Season Get(int seriesId, int seasonNumber);
|
||||
bool IsMonitored(int seriesId, int seasonNumber);
|
||||
List<Season> GetSeasonBySeries(int seriesId);
|
||||
}
|
||||
|
||||
public class SeasonRepository : BasicRepository<Season>, ISeasonRepository
|
||||
public class SeasonRepository : BasicRepository<Series>, ISeasonRepository
|
||||
{
|
||||
|
||||
public SeasonRepository(IDatabase database, IMessageAggregator messageAggregator)
|
||||
: base(database, messageAggregator)
|
||||
{
|
||||
}
|
||||
|
||||
public IList<int> GetSeasonNumbers(int seriesId)
|
||||
{
|
||||
return Query.Where(c => c.SeriesId == seriesId).Select(c => c.SeasonNumber).ToList();
|
||||
}
|
||||
|
||||
public Season Get(int seriesId, int seasonNumber)
|
||||
{
|
||||
return Query.Single(s => s.SeriesId == seriesId && s.SeasonNumber == seasonNumber);
|
||||
}
|
||||
|
||||
public bool IsMonitored(int seriesId, int seasonNumber)
|
||||
{
|
||||
var season = Query.SingleOrDefault(s => s.SeriesId == seriesId && s.SeasonNumber == seasonNumber);
|
||||
|
||||
if (season == null) return true;
|
||||
|
||||
return season.Monitored;
|
||||
}
|
||||
|
||||
public List<Season> GetSeasonBySeries(int seriesId)
|
||||
{
|
||||
return Query.Where(s => s.SeriesId == seriesId);
|
||||
return Query.Single(s => s.Id == seriesId).Seasons;
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
@ -34,12 +34,15 @@ namespace NzbDrone.Core.Tv
|
||||
public bool UseSceneNumbering { get; set; }
|
||||
public string TitleSlug { get; set; }
|
||||
public string Path { get; set; }
|
||||
public int Year { get; set; }
|
||||
|
||||
public string RootFolderPath { get; set; }
|
||||
|
||||
public DateTime? FirstAired { get; set; }
|
||||
public LazyLoaded<QualityProfile> QualityProfile { get; set; }
|
||||
|
||||
public List<Season> Seasons { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("[{0}][{1}]", TvdbId, Title.NullSafe());
|
||||
|
@ -37,18 +37,21 @@ namespace NzbDrone.Core.Tv
|
||||
private readonly IConfigService _configService;
|
||||
private readonly IMessageAggregator _messageAggregator;
|
||||
private readonly ISceneMappingService _sceneMappingService;
|
||||
private readonly IEpisodeService _episodeService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public SeriesService(ISeriesRepository seriesRepository,
|
||||
IConfigService configServiceService,
|
||||
IMessageAggregator messageAggregator,
|
||||
ISceneMappingService sceneMappingService,
|
||||
IEpisodeService episodeService,
|
||||
Logger logger)
|
||||
{
|
||||
_seriesRepository = seriesRepository;
|
||||
_configService = configServiceService;
|
||||
_messageAggregator = messageAggregator;
|
||||
_sceneMappingService = sceneMappingService;
|
||||
_episodeService = episodeService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@ -155,6 +158,11 @@ namespace NzbDrone.Core.Tv
|
||||
|
||||
public Series UpdateSeries(Series series)
|
||||
{
|
||||
foreach (var season in series.Seasons)
|
||||
{
|
||||
_episodeService.SetEpisodeMonitoredBySeason(series.Id, season.SeasonNumber, season.Monitored);
|
||||
}
|
||||
|
||||
return _seriesRepository.Update(series);
|
||||
}
|
||||
|
||||
|
@ -1,22 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using NzbDrone.Api.Episodes;
|
||||
using NzbDrone.Api.Seasons;
|
||||
using RestSharp;
|
||||
|
||||
namespace NzbDrone.Integration.Test.Client
|
||||
{
|
||||
public class SeasonClient : ClientBase<SeasonResource>
|
||||
{
|
||||
public SeasonClient(IRestClient restClient)
|
||||
: base(restClient)
|
||||
{
|
||||
}
|
||||
|
||||
public List<SeasonResource> GetSeasonsInSeries(int seriesId)
|
||||
{
|
||||
var request = BuildRequest("?seriesId=" + seriesId.ToString());
|
||||
return Get<List<SeasonResource>>(request);
|
||||
}
|
||||
}
|
||||
}
|
@ -27,7 +27,6 @@ namespace NzbDrone.Integration.Test
|
||||
protected ClientBase<HistoryResource> History;
|
||||
protected IndexerClient Indexers;
|
||||
protected EpisodeClient Episodes;
|
||||
protected SeasonClient Seasons;
|
||||
protected ClientBase<NamingConfigResource> NamingConfig;
|
||||
|
||||
private NzbDroneRunner _runner;
|
||||
@ -64,7 +63,6 @@ namespace NzbDrone.Integration.Test
|
||||
History = new ClientBase<HistoryResource>(RestClient);
|
||||
Indexers = new IndexerClient(RestClient);
|
||||
Episodes = new EpisodeClient(RestClient);
|
||||
Seasons = new SeasonClient(RestClient);
|
||||
NamingConfig = new ClientBase<NamingConfigResource>(RestClient, "config/naming");
|
||||
}
|
||||
|
||||
@ -75,5 +73,4 @@ namespace NzbDrone.Integration.Test
|
||||
_runner.KillAll();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -94,7 +94,6 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Client\ClientBase.cs" />
|
||||
<Compile Include="Client\SeasonClient.cs" />
|
||||
<Compile Include="Client\EpisodeClient.cs" />
|
||||
<Compile Include="Client\IndexerClient.cs" />
|
||||
<Compile Include="Client\ReleaseClient.cs" />
|
||||
@ -102,7 +101,6 @@
|
||||
<Compile Include="CommandIntegerationTests.cs" />
|
||||
<Compile Include="HistoryIntegrationTest.cs" />
|
||||
<Compile Include="NamingConfigTests.cs" />
|
||||
<Compile Include="SeasonIntegrationTests.cs" />
|
||||
<Compile Include="EpisodeIntegrationTests.cs" />
|
||||
<Compile Include="IndexerIntegrationFixture.cs" />
|
||||
<Compile Include="IntegrationTestDirectoryInfo.cs" />
|
||||
|
@ -1,61 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Api.Series;
|
||||
using System.Linq;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Integration.Test
|
||||
{
|
||||
[TestFixture]
|
||||
public class SeasonIntegrationTests : IntegrationTest
|
||||
{
|
||||
private SeriesResource GivenSeriesWithEpisodes()
|
||||
{
|
||||
var series = Series.Lookup("archer").First();
|
||||
|
||||
series.QualityProfileId = 1;
|
||||
series.Path = @"C:\Test\Archer".AsOsAgnostic();
|
||||
|
||||
series = Series.Post(series);
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (Seasons.GetSeasonsInSeries(series.Id).Count > 0)
|
||||
{
|
||||
return series;
|
||||
}
|
||||
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_able_to_get_all_seasons_in_series()
|
||||
{
|
||||
var series = GivenSeriesWithEpisodes();
|
||||
Seasons.GetSeasonsInSeries(series.Id).Count.Should().BeGreaterThan(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_able_to_get_a_single_season()
|
||||
{
|
||||
var series = GivenSeriesWithEpisodes();
|
||||
var seasons = Seasons.GetSeasonsInSeries(series.Id);
|
||||
|
||||
Seasons.Get(seasons.First().Id).Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_able_to_set_monitor_status_via_api()
|
||||
{
|
||||
var series = GivenSeriesWithEpisodes();
|
||||
var seasons = Seasons.GetSeasonsInSeries(series.Id);
|
||||
var updatedSeason = seasons.First();
|
||||
updatedSeason.Monitored = false;
|
||||
|
||||
Seasons.Put(updatedSeason).Monitored.Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
<select class="span6 x-root-folder">
|
||||
<select class="span4 x-root-folder">
|
||||
{{#if this}}
|
||||
{{#each this}}
|
||||
<option value="{{id}}">{{path}}</option>
|
||||
|
@ -0,0 +1,9 @@
|
||||
<select class="span2 x-starting-season">
|
||||
{{#each this}}
|
||||
{{#if_eq seasonNumber compare="0"}}
|
||||
<option value="{{seasonNumber}}">Specials</option>
|
||||
{{else}}
|
||||
<option value="{{seasonNumber}}">Season {{seasonNumber}}</option>
|
||||
{{/if_eq}}
|
||||
{{/each}}
|
||||
</select>
|
@ -3,7 +3,6 @@ define(
|
||||
[
|
||||
'app',
|
||||
'marionette',
|
||||
|
||||
'Quality/QualityProfileCollection',
|
||||
'AddSeries/RootFolders/Collection',
|
||||
'AddSeries/RootFolders/Layout',
|
||||
@ -15,13 +14,14 @@ define(
|
||||
|
||||
return Marionette.ItemView.extend({
|
||||
|
||||
template: 'AddSeries/SearchResultTemplate',
|
||||
template: 'AddSeries/SearchResultViewTemplate',
|
||||
|
||||
ui: {
|
||||
qualityProfile: '.x-quality-profile',
|
||||
rootFolder : '.x-root-folder',
|
||||
addButton : '.x-add',
|
||||
overview : '.x-overview'
|
||||
overview : '.x-overview',
|
||||
startingSeason: '.x-starting-season'
|
||||
},
|
||||
|
||||
events: {
|
||||
@ -57,6 +57,12 @@ define(
|
||||
this.ui.rootFolder.val(defaultRoot);
|
||||
}
|
||||
|
||||
var minSeasonNotZero = _.min(_.reject(this.model.get('seasons'), { seasonNumber: 0 }), 'seasonNumber');
|
||||
|
||||
if (minSeasonNotZero) {
|
||||
this.ui.startingSeason.val(minSeasonNotZero.seasonNumber);
|
||||
}
|
||||
|
||||
//TODO: make this work via onRender, FM?
|
||||
//works with onShow, but stops working after the first render
|
||||
this.ui.overview.dotdotdot({
|
||||
@ -117,15 +123,16 @@ define(
|
||||
|
||||
var quality = this.ui.qualityProfile.val();
|
||||
var rootFolderPath = this.ui.rootFolder.children(':selected').text();
|
||||
var startingSeason = this.ui.startingSeason.val();
|
||||
|
||||
this.model.set('qualityProfileId', quality);
|
||||
this.model.set('rootFolderPath', rootFolderPath);
|
||||
this.model.setSeasonPass(startingSeason);
|
||||
|
||||
var self = this;
|
||||
|
||||
SeriesCollection.add(this.model);
|
||||
|
||||
|
||||
this.model.save().done(function () {
|
||||
self.close();
|
||||
icon.removeClass('icon-spin icon-spinner disabled').addClass('icon-search');
|
||||
|
@ -2,36 +2,34 @@
|
||||
<div class="row">
|
||||
<div class="span2">
|
||||
<a href="{{traktUrl}}" target="_blank">
|
||||
<img class="new-series-poster" src="{{remotePoster}}"
|
||||
{{defaultImg}} >
|
||||
<img class="new-series-poster" src="{{remotePoster}}" {{defaultImg}} >
|
||||
</a>
|
||||
</div>
|
||||
<div class="span9">
|
||||
|
||||
<div class="row">
|
||||
<h2>{{title}}</h2>
|
||||
<h2>{{titleWithYear}}</h2>
|
||||
</div>
|
||||
<div class="row new-series-overview x-overview">
|
||||
{{overview}}
|
||||
</div>
|
||||
<div class="row">
|
||||
{{#if existing}}
|
||||
<div class="btn pull-right add-series disabled">
|
||||
<div class="btn add-series disabled pull-right">
|
||||
Already Exists
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="btn btn-success x-add pull-right add-series">
|
||||
Add
|
||||
<icon class="icon-plus"></icon>
|
||||
</div>
|
||||
{{#unless path}}
|
||||
{{> RootFolderSelectionPartial rootFolders}}
|
||||
{{/unless}}
|
||||
<div class='pull-right'>
|
||||
{{> QualityProfileSelectionPartial qualityProfiles}}
|
||||
|
||||
{{> StartingSeasonSelectionPartial seasons}}
|
||||
{{> QualityProfileSelectionPartial qualityProfiles}}
|
||||
|
||||
<div class="span1 btn btn-success x-add add-series pull-right">
|
||||
Add <i class="icon-plus"></i>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -113,4 +113,12 @@
|
||||
|
||||
.icon-nd-status:before {
|
||||
.icon(@circle);
|
||||
}
|
||||
|
||||
.icon-nd-monitored:before {
|
||||
.icon(@bookmark);
|
||||
}
|
||||
|
||||
.icon-nd-unmonitored:before {
|
||||
.icon(@bookmark-empty);
|
||||
}
|
@ -62,4 +62,12 @@ define(
|
||||
|
||||
return new Handlebars.SafeString('<span class="label label-info">{0} Seasons</span>'.format(seasonCount))
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('titleWithYear', function () {
|
||||
if (this.title.endsWith(' ({0})'.format(this.year))) {
|
||||
return this.title;
|
||||
}
|
||||
|
||||
return '{0} ({1})'.format(this.title, this.year);
|
||||
});
|
||||
});
|
||||
|
@ -24,16 +24,10 @@ define(
|
||||
this.series.show(new LoadingView());
|
||||
|
||||
this.seriesCollection = SeriesCollection;
|
||||
this.seasonCollection = new SeasonCollection();
|
||||
|
||||
var promise = this.seasonCollection.fetch();
|
||||
|
||||
promise.done(function () {
|
||||
self.series.show(new SeriesCollectionView({
|
||||
collection: self.seriesCollection,
|
||||
seasonCollection: self.seasonCollection
|
||||
}));
|
||||
});
|
||||
self.series.show(new SeriesCollectionView({
|
||||
collection: self.seriesCollection
|
||||
}));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -5,22 +5,6 @@ define(
|
||||
'SeasonPass/SeriesLayout'
|
||||
], function (Marionette, SeriesLayout) {
|
||||
return Marionette.CollectionView.extend({
|
||||
|
||||
itemView: SeriesLayout,
|
||||
|
||||
initialize: function (options) {
|
||||
|
||||
if (!options.seasonCollection) {
|
||||
throw 'seasonCollection is needed';
|
||||
}
|
||||
|
||||
this.seasonCollection = options.seasonCollection;
|
||||
},
|
||||
|
||||
itemViewOptions: function () {
|
||||
return {
|
||||
seasonCollection: this.seasonCollection
|
||||
};
|
||||
}
|
||||
itemView: SeriesLayout
|
||||
});
|
||||
});
|
||||
|
@ -2,11 +2,9 @@
|
||||
define(
|
||||
[
|
||||
'marionette',
|
||||
'backgrid',
|
||||
'Series/SeasonCollection',
|
||||
'Cells/ToggleCell',
|
||||
'Shared/Actioneer'
|
||||
], function (Marionette, Backgrid, SeasonCollection, ToggleCell, Actioneer) {
|
||||
], function (Marionette, SeasonCollection, Actioneer) {
|
||||
return Marionette.Layout.extend({
|
||||
template: 'SeasonPass/SeriesLayoutTemplate',
|
||||
|
||||
@ -19,48 +17,22 @@ define(
|
||||
events: {
|
||||
'change .x-season-select': '_seasonSelected',
|
||||
'click .x-expander' : '_expand',
|
||||
'click .x-latest' : '_latest'
|
||||
'click .x-latest' : '_latest',
|
||||
'click .x-monitored' : '_toggleSeasonMonitored'
|
||||
},
|
||||
|
||||
regions: {
|
||||
seasonGrid: '.x-season-grid'
|
||||
},
|
||||
|
||||
columns:
|
||||
[
|
||||
{
|
||||
name : 'monitored',
|
||||
label : '',
|
||||
cell : ToggleCell,
|
||||
trueClass : 'icon-bookmark',
|
||||
falseClass: 'icon-bookmark-empty',
|
||||
tooltip : 'Toggle monitored status',
|
||||
sortable : false
|
||||
},
|
||||
{
|
||||
name : 'seasonNumber',
|
||||
label: 'Season',
|
||||
cell : Backgrid.IntegerCell.extend({
|
||||
className: 'season-number-cell'
|
||||
})
|
||||
}
|
||||
],
|
||||
|
||||
initialize: function (options) {
|
||||
this.seasonCollection = options.seasonCollection.bySeries(this.model.get('id'));
|
||||
this.model.set('seasons', this.seasonCollection);
|
||||
initialize: function () {
|
||||
this.seasonCollection = new SeasonCollection(this.model.get('seasons'));
|
||||
this.expanded = false;
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
this.seasonGrid.show(new Backgrid.Grid({
|
||||
columns : this.columns,
|
||||
collection: this.seasonCollection,
|
||||
className : 'table table-condensed season-grid span5'
|
||||
}));
|
||||
|
||||
if (!this.expanded) {
|
||||
this.seasonGrid.$el.hide();
|
||||
this.ui.seasonGrid.hide();
|
||||
}
|
||||
|
||||
this._setExpanderIcon();
|
||||
@ -103,33 +75,51 @@ define(
|
||||
},
|
||||
|
||||
_latest: function () {
|
||||
var season = _.max(this.seasonCollection.models, function (model) {
|
||||
return model.get('seasonNumber');
|
||||
var season = _.max(this.model.get('seasons'), function (s) {
|
||||
return s.seasonNumber;
|
||||
});
|
||||
|
||||
//var seasonNumber = season.get('seasonNumber');
|
||||
|
||||
this._setMonitored(season.get('seasonNumber'))
|
||||
this._setMonitored(season.seasonNumber);
|
||||
},
|
||||
|
||||
_setMonitored: function (seasonNumber) {
|
||||
//TODO: use Actioneer?
|
||||
|
||||
var self = this;
|
||||
|
||||
var promise = $.ajax({
|
||||
url: this.seasonCollection.url + '/pass',
|
||||
type: 'POST',
|
||||
data: {
|
||||
seriesId: this.model.get('id'),
|
||||
seasonNumber: seasonNumber
|
||||
}
|
||||
});
|
||||
this.model.setSeasonPass(seasonNumber);
|
||||
|
||||
var promise = this.model.save();
|
||||
|
||||
promise.done(function (data) {
|
||||
self.seasonCollection = new SeasonCollection(data);
|
||||
self.render();
|
||||
});
|
||||
},
|
||||
|
||||
_toggleSeasonMonitored: function (e) {
|
||||
var seasonNumber = 0;
|
||||
var element;
|
||||
|
||||
if (e.target.localName === 'i') {
|
||||
seasonNumber = parseInt($(e.target).parent('td').attr('data-season-number'));
|
||||
element = $(e.target);
|
||||
}
|
||||
|
||||
else {
|
||||
seasonNumber = parseInt($(e.target).attr('data-season-number'));
|
||||
element = $(e.target).children('i');
|
||||
}
|
||||
|
||||
this.model.setSeasonMonitored(seasonNumber);
|
||||
|
||||
Actioneer.SaveModel({
|
||||
element: element,
|
||||
context: this,
|
||||
always : this._afterToggleSeasonMonitored
|
||||
});
|
||||
},
|
||||
|
||||
_afterToggleSeasonMonitored: function () {
|
||||
this.render();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -12,12 +12,12 @@
|
||||
<span class="span3">
|
||||
<select class="x-season-select season-select">
|
||||
<option value="-1">Select season...</option>
|
||||
{{#each seasons.models}}
|
||||
{{#if_eq attributes.seasonNumber compare="0"}}
|
||||
<option value="{{attributes.seasonNumber}}">Specials</option>
|
||||
{{else}}
|
||||
<option value="{{attributes.seasonNumber}}">Season {{attributes.seasonNumber}}</option>
|
||||
{{/if_eq}}
|
||||
{{#each seasons}}
|
||||
{{#if_eq seasonNumber compare="0"}}
|
||||
<option value="{{seasonNumber}}">Specials</option>
|
||||
{{else}}
|
||||
<option value="{{seasonNumber}}">Season {{seasonNumber}}</option>
|
||||
{{/if_eq}}
|
||||
{{/each}}
|
||||
</select>
|
||||
|
||||
@ -36,7 +36,36 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="span11">
|
||||
<div class="x-season-grid season-grid"></div>
|
||||
<div class="x-season-grid season-grid">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th class="sortable">Season</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each seasons}}
|
||||
<tr>
|
||||
<td class="toggle-cell x-monitored" data-season-number="{{seasonNumber}}">
|
||||
{{#if monitored}}
|
||||
<i class="icon-nd-monitored"></i>
|
||||
{{else}}
|
||||
<i class="icon-nd-unmonitored"></i>
|
||||
{{/if}}
|
||||
</td>
|
||||
<td>
|
||||
{{#if_eq seasonNumber compare="0"}}
|
||||
Specials
|
||||
{{else}}
|
||||
Season {{seasonNumber}}
|
||||
{{/if_eq}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -117,8 +117,10 @@ define(
|
||||
_seasonMonitored: function () {
|
||||
var name = 'monitored';
|
||||
this.model.set(name, !this.model.get(name));
|
||||
this.series.setSeasonMonitored(this.model.get('seasonNumber'));
|
||||
|
||||
Actioneer.SaveModel({
|
||||
model : this.series,
|
||||
context: this,
|
||||
element: this.ui.seasonMonitored,
|
||||
always : this._afterSeasonMonitored
|
||||
|
@ -113,12 +113,12 @@ define(
|
||||
this.ui.monitored.removeClass('icon-spin icon-spinner');
|
||||
|
||||
if (this.model.get('monitored')) {
|
||||
this.ui.monitored.addClass('icon-bookmark');
|
||||
this.ui.monitored.removeClass('icon-bookmark-empty');
|
||||
this.ui.monitored.addClass('icon-nd-monitored');
|
||||
this.ui.monitored.removeClass('icon-nd-unmonitored');
|
||||
}
|
||||
else {
|
||||
this.ui.monitored.addClass('icon-bookmark-empty');
|
||||
this.ui.monitored.removeClass('icon-bookmark');
|
||||
this.ui.monitored.addClass('icon-nd-unmonitored');
|
||||
this.ui.monitored.removeClass('icon-nd-monitored');
|
||||
}
|
||||
},
|
||||
|
||||
@ -176,11 +176,11 @@ define(
|
||||
|
||||
this.seasons.show(new LoadingView());
|
||||
|
||||
this.seasonCollection = new SeasonCollection();
|
||||
this.seasonCollection = new SeasonCollection(this.model.get('seasons'));
|
||||
this.episodeCollection = new EpisodeCollection({ seriesId: this.model.id });
|
||||
this.episodeFileCollection = new EpisodeFileCollection({ seriesId: this.model.id });
|
||||
|
||||
$.when(this.episodeCollection.fetch(), this.episodeFileCollection.fetch(), this.seasonCollection.fetch({data: { seriesId: this.model.id }})).done(function () {
|
||||
$.when(this.episodeCollection.fetch(), this.episodeFileCollection.fetch()).done(function () {
|
||||
var seasonCollectionView = new SeasonCollectionView({
|
||||
collection : self.seasonCollection,
|
||||
episodeCollection: self.episodeCollection,
|
||||
|
@ -5,21 +5,10 @@ define(
|
||||
'Series/SeasonModel'
|
||||
], function (Backbone, SeasonModel) {
|
||||
return Backbone.Collection.extend({
|
||||
url : window.ApiRoot + '/season',
|
||||
model: SeasonModel,
|
||||
|
||||
comparator: function (season) {
|
||||
return -season.get('seasonNumber');
|
||||
},
|
||||
|
||||
bySeries: function (series) {
|
||||
var filtered = this.filter(function (season) {
|
||||
return season.get('seriesId') === series;
|
||||
});
|
||||
|
||||
var SeasonCollection = require('Series/SeasonCollection');
|
||||
|
||||
return new SeasonCollection(filtered);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -14,6 +14,25 @@ define(
|
||||
episodeCount : 0,
|
||||
isExisting : false,
|
||||
status : 0
|
||||
},
|
||||
|
||||
setSeasonMonitored: function (seasonNumber) {
|
||||
_.each(this.get('seasons'), function (season) {
|
||||
if (season.seasonNumber === seasonNumber) {
|
||||
season.monitored = !season.monitored;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
setSeasonPass: function (seasonNumber) {
|
||||
_.each(this.get('seasons'), function (season) {
|
||||
if (season.seasonNumber >= seasonNumber) {
|
||||
season.monitored = true;
|
||||
}
|
||||
else {
|
||||
season.monitored = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -256,6 +256,7 @@
|
||||
.clickable;
|
||||
line-height: 30px;
|
||||
margin-left: 8px;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.season-grid {
|
||||
|
@ -33,7 +33,9 @@ define(
|
||||
this._showStartMessage(options);
|
||||
this._setSpinnerOnElement(options);
|
||||
|
||||
var promise = options.context.model.save();
|
||||
var model = options.model ? options.model : options.context.model;
|
||||
|
||||
var promise = model.save();
|
||||
this._handlePromise(promise, options);
|
||||
},
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user