1
0
mirror of https://github.com/Radarr/Radarr.git synced 2024-11-04 10:02:40 +01:00

Series/Index started in backbone

This commit is contained in:
Mark McDowall 2013-02-09 02:04:18 -08:00 committed by kay.one
parent ace7910f2a
commit eb18d1c4a1
20 changed files with 204 additions and 42 deletions

View File

@ -1,4 +1,5 @@
using System.Linq;
using System;
using System.Linq;
using AutoMapper;
using Autofac;
using NLog;
@ -9,7 +10,9 @@
using NzbDrone.Api.QualityProfiles;
using NzbDrone.Api.QualityType;
using NzbDrone.Api.Resolvers;
using NzbDrone.Api.Series;
using NzbDrone.Core;
using NzbDrone.Core.Helpers;
using NzbDrone.Core.Repository.Quality;
namespace NzbDrone.Api
@ -32,25 +35,30 @@ protected override void ApplicationStartup(ILifetimeScope container, IPipelines
public static void InitializeAutomapper()
{
//QualityProfiles
Mapper.CreateMap<QualityProfileModel, QualityProfile>()
.ForMember(dest => dest.QualityProfileId, opt => opt.MapFrom(src => src.Id))
.ForMember(dest => dest.Allowed,
opt => opt.ResolveUsing<QualitiesToAllowedResolver>().FromMember(src => src.Qualities));
Mapper.CreateMap<QualityProfile, QualityProfileModel>()
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.QualityProfileId))
.ForMember(dest => dest.Qualities,
opt => opt.ResolveUsing<AllowedToQualitiesResolver>().FromMember(src => src.Allowed));
Mapper.CreateMap<QualityProfileModel, QualityProfile>()
.ForMember(dest => dest.QualityProfileId, opt => opt.MapFrom(src => src.Id))
.ForMember(dest => dest.Allowed,
opt => opt.ResolveUsing<QualitiesToAllowedResolver>().FromMember(src => src.Qualities));
Mapper.CreateMap<QualityTypes, QualityProfileType>()
.ForMember(dest => dest.Allowed, opt => opt.Ignore());
//QualityTypes
Mapper.CreateMap<Core.Repository.Quality.QualityType, QualityTypeModel>()
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.QualityTypeId));
Mapper.CreateMap<QualityTypeModel, Core.Repository.Quality.QualityType>()
.ForMember(dest => dest.QualityTypeId, opt => opt.MapFrom(src => src.Id));
Mapper.CreateMap<Core.Repository.Quality.QualityType, QualityTypeModel>()
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.QualityTypeId));
//Series
Mapper.CreateMap<Core.Repository.Series, SeriesModel>()
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.SeriesId));
//.ForMember(dest => dest.BacklogSetting, opt => opt.MapFrom(src => Convert.ToInt32(src.BacklogSetting)));
}
protected override ILifetimeScope GetApplicationContainer()

View File

@ -96,6 +96,7 @@
<Compile Include="Extentions\NancyJsonSerializer.cs" />
<Compile Include="Extentions\Serializer.cs" />
<Compile Include="RootFolders\RootFolderModule.cs" />
<Compile Include="Series\SeriesModel.cs" />
<Compile Include="Series\SeriesModule.cs" />
<Compile Include="Series\SeriesLookupModule.cs" />
<Compile Include="ErrorManagment\ApiException.cs" />

View File

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NzbDrone.Core.Model;
namespace NzbDrone.Api.Series
{
public class SeriesModel
{
public Int32 Id { get; set; }
//Todo: Sorters should be done completely on the client
//Todo: Is there an easy way to keep IgnoreArticlesWhenSorting in sync between, Series, History, Missing?
//View Only
public String Title { get; set; }
public Int32 SeasonsCount { get; set; }
public Int32 EpisodeCount { get; set; }
public Int32 EpisodeFileCount { get; set; }
public String Status { get; set; }
public String AirsDayOfWeek { get; set; }
public String QualityProfileName { get; set; }
public String Overview { get; set; }
public Int32 Episodes { get; set; }
public Boolean HasBanner { get; set; }
public DateTime NextAiring { get; set; }
public String Details { get; set; }
public String Network { get; set; }
public String AirTime { get; set; }
public String Language { get; set; }
public Int32 SeasonCount { get; set; }
public Int32 UtcOffset { get; set; }
//View & Edit
public String Path { get; set; }
public Int32 QualityProfileId { get; set; }
//Editing Only
public Boolean SeasonFolder { get; set; }
public Boolean Monitored { get; set; }
public BacklogSettingType BacklogSetting { get; set; }
public DateTime? CustomStartDate { get; set; }
}
}

View File

@ -1,10 +1,14 @@
using System.Linq;
using System.Collections.Generic;
using System.Linq;
using AutoMapper;
using FluentValidation;
using Nancy;
using NzbDrone.Api.Extentions;
using NzbDrone.Api.QualityProfiles;
using NzbDrone.Common;
using NzbDrone.Core.Jobs;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.Core;
namespace NzbDrone.Api.Series
{
@ -12,15 +16,26 @@ public class SeriesModule : NzbDroneApiModule
{
private readonly SeriesProvider _seriesProvider;
private readonly JobProvider _jobProvider;
private readonly ConfigProvider _configProvider;
public SeriesModule(SeriesProvider seriesProvider, JobProvider jobProvider)
public SeriesModule(SeriesProvider seriesProvider, JobProvider jobProvider,
ConfigProvider configProvider)
: base("/Series")
{
_seriesProvider = seriesProvider;
_jobProvider = jobProvider;
_configProvider = configProvider;
Get["/"] = x => AllSeries();
Post["/"] = x => AddSeries();
}
private Response AllSeries()
{
var series = _seriesProvider.GetAllSeriesWithEpisodeCount().ToList();
var seriesModels = Mapper.Map<List<Core.Repository.Series>, List<SeriesModel>>(series);
return seriesModels.AsResponse();
}
private Response AddSeries()
{
@ -31,7 +46,6 @@ private Response AddSeries()
//(we can just create the folder and it won't blow up if it already exists)
//We also need to remove any special characters from the filename before attempting to create it
_seriesProvider.AddSeries("", request.Path, request.SeriesId, request.QualityProfileId, null);
_jobProvider.QueueJob(typeof(ImportNewSeriesJob));
@ -39,7 +53,6 @@ private Response AddSeries()
}
}
public class SeriesValidator : AbstractValidator<Core.Repository.Series>
{
private readonly DiskProvider _diskProvider;
@ -58,7 +71,5 @@ public SeriesValidator()
RuleFor(s => s.QualityProfileId).GreaterThan(0);
});
}
}
}

View File

@ -58,7 +58,7 @@ public virtual IList<Series> GetAllSeriesWithEpisodeCount()
var series = _database
.Fetch<Series, QualityProfile>(@"SELECT Series.SeriesId, Series.Title, Series.CleanTitle, Series.Status, Series.Overview, Series.AirsDayOfWeek, Series.AirTimes,
Series.Language, Series.Path, Series.Monitored, Series.QualityProfileId, Series.SeasonFolder, Series.BacklogSetting, Series.Network,
SUM(CASE WHEN Ignored = 0 AND Airdate <= @0 THEN 1 ELSE 0 END) AS EpisodeCount,
Series.UtcOffset, Series.CustomStartDate, SUM(CASE WHEN Ignored = 0 AND Airdate <= @0 THEN 1 ELSE 0 END) AS EpisodeCount,
SUM(CASE WHEN Episodes.Ignored = 0 AND Episodes.EpisodeFileId > 0 AND Episodes.AirDate <= @0 THEN 1 ELSE 0 END) as EpisodeFileCount,
MAX(Episodes.SeasonNumber) as SeasonCount, MIN(CASE WHEN AirDate < @0 OR Ignored = 1 THEN NULL ELSE AirDate END) as NextAiring,
QualityProfiles.QualityProfileId, QualityProfiles.Name, QualityProfiles.Cutoff, QualityProfiles.SonicAllowed
@ -68,7 +68,8 @@ FROM Series
WHERE Series.LastInfoSync IS NOT NULL
GROUP BY Series.SeriesId, Series.Title, Series.CleanTitle, Series.Status, Series.Overview, Series.AirsDayOfWeek, Series.AirTimes,
Series.Language, Series.Path, Series.Monitored, Series.QualityProfileId, Series.SeasonFolder, Series.BacklogSetting, Series.Network,
QualityProfiles.QualityProfileId, QualityProfiles.Name, QualityProfiles.Cutoff, QualityProfiles.SonicAllowed", DateTime.Today);
Series.UtcOffset, Series.CustomStartDate,
QualityProfiles.QualityProfileId, QualityProfiles.Name, QualityProfiles.Cutoff, QualityProfiles.SonicAllowed",DateTime.Today);
return series;
}
@ -99,7 +100,7 @@ public virtual Series UpdateSeriesInfo(int seriesId)
series.SeriesId = tvDbSeries.Id;
series.Title = tvDbSeries.SeriesName;
series.AirTimes = CleanAirsTime(tvDbSeries.AirsTime);
series.AirTime = CleanAirsTime(tvDbSeries.AirsTime);
series.AirsDayOfWeek = tvDbSeries.AirsDayOfWeek;
series.Overview = tvDbSeries.Overview;
series.Status = tvDbSeries.Status;

View File

@ -26,7 +26,8 @@ public class Series
[DisplayName("Air on")]
public DayOfWeek? AirsDayOfWeek { get; set; }
public String AirTimes { get; set; }
[Column("AirTimes")]
public String AirTime { get; set; }
public string Language { get; set; }

View File

@ -237,7 +237,7 @@ private List<SeriesModel> GetSeriesModels(IList<Series> seriesInDb)
EpisodeFileCount = s.EpisodeFileCount,
NextAiring = s.NextAiring == null ? String.Empty : s.NextAiring.Value.ToBestDateString(),
NextAiringSorter = s.NextAiring == null ? new DateTime(9999, 12, 31).ToString("o", CultureInfo.InvariantCulture) : s.NextAiring.Value.ToString("o", CultureInfo.InvariantCulture),
AirTime = s.AirTimes,
AirTime = s.AirTime,
CustomStartDate = s.CustomStartDate.HasValue ? s.CustomStartDate.Value.ToString("yyyy-MM-dd") : String.Empty
}).ToList();

View File

@ -52,9 +52,9 @@ private List<UpcomingEpisodeModel> GetUpcomingEpisodeModels(List<Episode> episod
EpisodeNumbering = String.Format("{0}x{1:00}", u.SeasonNumber, u.EpisodeNumber),
Title = u.Title,
Overview = u.Overview,
AirDateTime = GetDateTime(u.AirDate.Value, u.Series.AirTimes),
AirDateTime = GetDateTime(u.AirDate.Value, u.Series.AirTime),
AirDate = u.AirDate.Value.ToBestDateString(),
AirTime = String.IsNullOrEmpty(u.Series.AirTimes) ? "?" : Convert.ToDateTime(u.Series.AirTimes).ToShortTimeString(),
AirTime = String.IsNullOrEmpty(u.Series.AirTime) ? "?" : Convert.ToDateTime(u.Series.AirTime).ToShortTimeString(),
Status = u.Status.ToString()
}).OrderBy(e => e.AirDateTime).ToList();
}

View File

@ -409,6 +409,12 @@
<Content Include="_backboneApp\JsLibraries\backbone.js" />
<Content Include="_backboneApp\JsLibraries\backbone.marionette.js" />
<Content Include="_backboneApp\AddSeries\addSeriesLayoutTemplate.html" />
<Content Include="_backboneApp\Series\Index\SeriesCollectionTemplate.html" />
<Content Include="_backboneApp\Series\Index\IndexLayoutTemplate.html" />
<Content Include="_backboneApp\Series\Index\IndexLayout.js" />
<Content Include="_backboneApp\Series\Index\SeriesItemTemplate.html" />
<Content Include="_backboneApp\Series\Index\SeriesItemView.js" />
<Content Include="_backboneApp\Series\SeriesCollection.js" />
<Content Include="_backboneApp\Series\SeriesModel.js" />
<Content Include="_backboneApp\Shared\AutoComplete.js" />
<Content Include="_backboneApp\Shared\NotificationModel.js" />

View File

@ -56,8 +56,6 @@ NzbDrone.AddSeries.SearchItemView = Backbone.Marionette.ItemView.extend({
}
});
}
});
NzbDrone.AddSeries.SearchResultView = Backbone.Marionette.CollectionView.extend({

View File

@ -30,6 +30,7 @@ public void Configure(BundleCollection bundles)
bundles.Add<ScriptBundle>("~/_backboneApp/JsLibraries/backbone.js");
bundles.Add<ScriptBundle>(NZBDRONE, new[]{
APP_PATH + "\\Series\\Index\\IndexLayout.js",
APP_PATH + "\\AddSeries\\AddSeriesLayout.js",
APP_PATH + "\\Shared\\NotificationView.js",

View File

@ -0,0 +1,33 @@
'use strict;'
/// <reference path="../../app.js" />
/// <reference path="../SeriesCollection.js" />
/// <reference path="SeriesItemView.js" />
NzbDrone.Series.IndexLayout = Backbone.Marionette.Layout.extend({
template: 'Series/Index/IndexLayoutTemplate',
route: 'Series/index',
ui: {
edit: '.edit-series',
delele: '.delete-series'
},
regions: {
main: '#series',
},
collection: new NzbDrone.Series.SeriesCollection(),
initialize: function (options) {
},
onRender: function () {
console.log('binding auto complete');
this.collection.fetch();
//Show things
this.main.show(new NzbDrone.Series.Index.SeriesCollectionView({ collection: this.collection }));
},
});

View File

@ -0,0 +1 @@
<div id="series" class="result-list span18 offset1"></div>

View File

@ -0,0 +1,13 @@
<thead>
<tr>
<th></th>
<th>Title</th>
<th>Seasons</th>
<th>Quality</th>
<th>Network</th>
<th>Next Airing</th>
<th>Episodes</th>
<th></th>
</tr>
</thead>
<tbody></tbody>

View File

@ -0,0 +1,15 @@
<td name="status"></td>
<td name="title"></td>
<td name="seasonCount"></td>
<td name="qualityProfileName"></td>
<td name="network"></td>
<td name="nextAiring"></td>
<td name="EpisodeCount">
{{#if episodeFileCount}}
{{episodeFileCount}}
{{else}}
0
{{/if}}
/ {{episodeCount}}
<td>
<td>Edit/Delete</td>

View File

@ -0,0 +1,30 @@
'use strict';
/*global NzbDrone, Backbone*/
/// <reference path="../../app.js" />
/// <reference path="../SeriesModel.js" />
/// <reference path="../SeriesCollection.js" />
NzbDrone.Series.Index.SeriesItemView = Backbone.Marionette.ItemView.extend({
template: 'Series/Index/SeriesItemTemplate',
tagName: 'tr',
events: {
'click .x-remove': 'removeSeries',
},
onRender: function () {
NzbDrone.ModelBinder.bind(this.model, this.el);
},
removeSeries: function () {
this.model.destroy({ wait: true });
this.model.collection.remove(this.model);
},
});
NzbDrone.Series.Index.SeriesCollectionView = Backbone.Marionette.CompositeView.extend({
itemView: NzbDrone.Series.Index.SeriesItemView,
template: 'Series/Index/SeriesCollectionTemplate',
tagName: 'table',
className: 'table table-hover',
});

View File

@ -0,0 +1,7 @@
/// <reference path="../app.js" />
/// <reference path="SeriesModel.js" />
NzbDrone.Series.SeriesCollection = Backbone.Collection.extend({
url: NzbDrone.Constants.ApiRoot + '/series',
model: NzbDrone.Series.SeriesModel,
});

View File

@ -1,10 +1,3 @@
NzbDrone.Series.SeriesModel = Backbone.Model.extend({
url: NzbDrone.Constants.ApiRoot + '/series'
});
NzbDrone.Series.SeriesCollection = Backbone.Collection.extend({
model: NzbDrone.Series.SeriesModel,
url: NzbDrone.Constants.ApiRoot + '/series',
});

View File

@ -18,6 +18,7 @@ if (typeof console === undefined) {
NzbDrone = new Backbone.Marionette.Application();
NzbDrone.Series = {};
NzbDrone.Series.Index = {};
NzbDrone.AddSeries = {};
NzbDrone.AddSeries.New = {};
NzbDrone.AddSeries.Existing = {};
@ -33,7 +34,6 @@ _.templateSettings = {
NzbDrone.ModelBinder = new Backbone.ModelBinder();
NzbDrone.Constants = {
ApiRoot: '/api'
};
@ -42,38 +42,37 @@ NzbDrone.Events = {
DisplayInMainRegion: 'DisplayInMainRegion'
};
NzbDrone.Controller = Backbone.Marionette.Controller.extend({
addSeries: function (action, query) {
NzbDrone.mainRegion.show(new NzbDrone.AddSeries.AddSeriesLayout(this, action, query));
},
series: function (action, query) {
NzbDrone.mainRegion.show(new NzbDrone.Series.IndexLayout(this, action, query));
},
notFound: function () {
alert('route not found');
}
});
NzbDrone.Router = Backbone.Marionette.AppRouter.extend({
controller: new NzbDrone.Controller(),
// "someMethod" must exist at controller.someMethod
appRoutes: {
'series/index': 'series',
'series/add': 'addSeries',
'series/add/:action(/:query)': 'addSeries',
':whatever': 'notFound'
}
});
NzbDrone.addInitializer(function (options) {
console.log('starting application');
NzbDrone.addRegions({
mainRegion: '#main-region',
notificationRegion: '#notification-region'
@ -81,6 +80,4 @@ NzbDrone.addInitializer(function (options) {
NzbDrone.Router = new NzbDrone.Router();
Backbone.history.start();
});

View File

@ -1,6 +1,6 @@
<SolutionConfiguration>
<FileVersion>1</FileVersion>
<AutoEnableOnStartup>True</AutoEnableOnStartup>
<AutoEnableOnStartup>False</AutoEnableOnStartup>
<AllowParallelTestExecution>true</AllowParallelTestExecution>
<FrameworkUtilisationTypeForNUnit>UseDynamicAnalysis</FrameworkUtilisationTypeForNUnit>
<FrameworkUtilisationTypeForGallio>Disabled</FrameworkUtilisationTypeForGallio>