New: Series lists will auto update when files are imported/deleted

pull/4/head
Mark McDowall 11 years ago
parent 7261a4bd71
commit e47b4c7686

@ -2,23 +2,39 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using FluentValidation; using FluentValidation;
using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.MediaCover; using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.SeriesStats; using NzbDrone.Core.SeriesStats;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Api.Validation; using NzbDrone.Api.Validation;
using NzbDrone.Api.Mapping; using NzbDrone.Api.Mapping;
using NzbDrone.Core.Tv.Events;
namespace NzbDrone.Api.Series namespace NzbDrone.Api.Series
{ {
public class SeriesModule : NzbDroneRestModule<SeriesResource> public class SeriesModule : NzbDroneRestModuleWithSignalR<SeriesResource, Core.Tv.Series>,
IHandle<EpisodeImportedEvent>,
IHandle<EpisodeFileDeletedEvent>,
IHandle<SeriesUpdatedEvent>,
IHandle<SeriesEditedEvent>,
IHandle<SeriesDeletedEvent>
{ {
private readonly ICommandExecutor _commandExecutor;
private readonly ISeriesService _seriesService; private readonly ISeriesService _seriesService;
private readonly ISeriesStatisticsService _seriesStatisticsService; private readonly ISeriesStatisticsService _seriesStatisticsService;
private readonly IMapCoversToLocal _coverMapper; private readonly IMapCoversToLocal _coverMapper;
public SeriesModule(ISeriesService seriesService, ISeriesStatisticsService seriesStatisticsService, IMapCoversToLocal coverMapper) public SeriesModule(ICommandExecutor commandExecutor,
: base("/Series") ISeriesService seriesService,
ISeriesStatisticsService seriesStatisticsService,
IMapCoversToLocal coverMapper)
: base(commandExecutor)
{ {
_commandExecutor = commandExecutor;
_seriesService = seriesService; _seriesService = seriesService;
_seriesStatisticsService = seriesStatisticsService; _seriesStatisticsService = seriesStatisticsService;
_coverMapper = coverMapper; _coverMapper = coverMapper;
@ -74,6 +90,8 @@ namespace NzbDrone.Api.Series
private void UpdateSeries(SeriesResource seriesResource) private void UpdateSeries(SeriesResource seriesResource)
{ {
GetNewId<Core.Tv.Series>(_seriesService.UpdateSeries, seriesResource); GetNewId<Core.Tv.Series>(_seriesService.UpdateSeries, seriesResource);
BroadcastResourceChange(ModelAction.Updated, seriesResource);
} }
private void DeleteSeries(int id) private void DeleteSeries(int id)
@ -119,5 +137,32 @@ namespace NzbDrone.Api.Series
resource.EpisodeFileCount = seriesStatistics.EpisodeFileCount; resource.EpisodeFileCount = seriesStatistics.EpisodeFileCount;
resource.NextAiring = seriesStatistics.NextAiring; resource.NextAiring = seriesStatistics.NextAiring;
} }
public void Handle(EpisodeImportedEvent message)
{
BroadcastResourceChange(ModelAction.Updated, message.ImportedEpisode.SeriesId);
}
public void Handle(EpisodeFileDeletedEvent message)
{
if (message.ForUpgrade) return;
BroadcastResourceChange(ModelAction.Updated, message.EpisodeFile.SeriesId);
}
public void Handle(SeriesUpdatedEvent message)
{
BroadcastResourceChange(ModelAction.Updated, message.Series.Id);
}
public void Handle(SeriesEditedEvent message)
{
BroadcastResourceChange(ModelAction.Updated, message.Series.Id);
}
public void Handle(SeriesDeletedEvent message)
{
BroadcastResourceChange(ModelAction.Deleted, message.Series.InjectTo<SeriesResource>());
}
} }
} }

@ -489,6 +489,7 @@
<Compile Include="ThingiProvider\ProviderRepository.cs" /> <Compile Include="ThingiProvider\ProviderRepository.cs" />
<Compile Include="ThingiProvider\ProviderFactory.cs" /> <Compile Include="ThingiProvider\ProviderFactory.cs" />
<Compile Include="Tv\EpisodeService.cs" /> <Compile Include="Tv\EpisodeService.cs" />
<Compile Include="Tv\Events\SeriesEditedEvent.cs" />
<Compile Include="Tv\Events\SeriesRefreshStartingEvent.cs" /> <Compile Include="Tv\Events\SeriesRefreshStartingEvent.cs" />
<Compile Include="Tv\Events\EpisodeInfoDeletedEvent.cs" /> <Compile Include="Tv\Events\EpisodeInfoDeletedEvent.cs" />
<Compile Include="Tv\Events\EpisodeInfoUpdatedEvent.cs" /> <Compile Include="Tv\Events\EpisodeInfoUpdatedEvent.cs" />

@ -0,0 +1,14 @@
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.Tv.Events
{
public class SeriesEditedEvent : IEvent
{
public Series Series { get; private set; }
public SeriesEditedEvent(Series series)
{
Series = series;
}
}
}

@ -136,7 +136,10 @@ namespace NzbDrone.Core.Tv
} }
} }
return _seriesRepository.Update(series); var updatedSeries = _seriesRepository.Update(series);
_eventAggregator.PublishEvent(new SeriesEditedEvent(updatedSeries));
return updatedSeries;
} }
public List<Series> UpdateSeries(List<Series> series) public List<Series> UpdateSeries(List<Series> series)
@ -148,6 +151,8 @@ namespace NzbDrone.Core.Tv
var folderName = new DirectoryInfo(s.Path).Name; var folderName = new DirectoryInfo(s.Path).Name;
s.Path = Path.Combine(s.RootFolderPath, folderName); s.Path = Path.Combine(s.RootFolderPath, folderName);
} }
_eventAggregator.PublishEvent(new SeriesEditedEvent(s));
} }
_seriesRepository.UpdateMany(series); _seriesRepository.UpdateMany(series);

@ -1,9 +1,9 @@
'use strict'; 'use strict';
define( define(
[ [
'backgrid' 'Cells/NzbDroneCell'
], function (Backgrid) { ], function (NzbDroneCell) {
return Backgrid.Cell.extend({ return NzbDroneCell.extend({
className: 'series-status-cell', className: 'series-status-cell',
render: function () { render: function () {
@ -13,20 +13,24 @@ define(
if (status === 'ended') { if (status === 'ended') {
this.$el.html('<i class="icon-stop grid-icon" title="Ended"></i>'); this.$el.html('<i class="icon-stop grid-icon" title="Ended"></i>');
this.model.set('statusWeight', 3); this._setStatusWeight(3);
} }
else if (!monitored) { else if (!monitored) {
this.$el.html('<i class="icon-pause grid-icon" title="Not Monitored"></i>'); this.$el.html('<i class="icon-pause grid-icon" title="Not Monitored"></i>');
this.model.set('statusWeight', 2); this._setStatusWeight(2);
} }
else { else {
this.$el.html('<i class="icon-play grid-icon" title="Continuing"></i>'); this.$el.html('<i class="icon-play grid-icon" title="Continuing"></i>');
this.model.set('statusWeight', 1); this._setStatusWeight(1);
} }
return this; return this;
},
_setStatusWeight: function (weight) {
this.model.set('statusWeight', weight, {silent: true});
} }
}); });
}); });

@ -32,7 +32,7 @@ define(
this.prototype._makeFullCollection = function (models, options) { this.prototype._makeFullCollection = function (models, options) {
var self = this; var self = this;
self.shadowCollection = originalMakeFullCollection.apply(this, [models, options]); self.shadowCollection = originalMakeFullCollection.call(this, models, options);
var filterModel = function(model) { var filterModel = function(model) {
if (!self.state.filterKey || !self.state.filterValue) if (!self.state.filterKey || !self.state.filterValue)
@ -46,12 +46,10 @@ define(
}; };
var filteredModels = self.shadowCollection.filtered(); var filteredModels = self.shadowCollection.filtered();
var fullCollection = originalMakeFullCollection.call(this, filteredModels, options);
var fullCollection = originalMakeFullCollection.apply(this, [filteredModels, options]);
fullCollection.resetFiltered = function(options) { fullCollection.resetFiltered = function(options) {
Backbone.Collection.prototype.reset.apply(this, [self.shadowCollection.filtered(), options]); Backbone.Collection.prototype.reset.call(this, self.shadowCollection.filtered(), options);
}; };
fullCollection.reset = function (models, options) { fullCollection.reset = function (models, options) {

@ -22,6 +22,12 @@ define(
return; return;
} }
if (options.action === 'deleted') {
collection.remove(new collection.model(options.resource, {parse: true}));
return;
}
var model = new collection.model(options.resource, {parse: true}); var model = new collection.model(options.resource, {parse: true});
//updateOnly will prevent the collection from adding a new item //updateOnly will prevent the collection from adding a new item

@ -1,6 +1,7 @@
'use strict'; 'use strict';
define( define(
[ [
'underscore',
'marionette', 'marionette',
'backgrid', 'backgrid',
'Series/Index/Posters/SeriesPostersCollectionView', 'Series/Index/Posters/SeriesPostersCollectionView',
@ -16,9 +17,9 @@ define(
'Cells/SeriesStatusCell', 'Cells/SeriesStatusCell',
'Series/Index/FooterView', 'Series/Index/FooterView',
'Series/Index/FooterModel', 'Series/Index/FooterModel',
'Shared/Toolbar/ToolbarLayout', 'Shared/Toolbar/ToolbarLayout'
'underscore' ], function (_,
], function (Marionette, Marionette,
Backgrid, Backgrid,
PosterCollectionView, PosterCollectionView,
ListCollectionView, ListCollectionView,
@ -33,8 +34,7 @@ define(
SeriesStatusCell, SeriesStatusCell,
FooterView, FooterView,
FooterModel, FooterModel,
ToolbarLayout, ToolbarLayout) {
_) {
return Marionette.Layout.extend({ return Marionette.Layout.extend({
template: 'Series/Index/SeriesIndexLayoutTemplate', template: 'Series/Index/SeriesIndexLayoutTemplate',
@ -131,8 +131,25 @@ define(
initialize: function () { initialize: function () {
this.seriesCollection = SeriesCollection.clone(); this.seriesCollection = SeriesCollection.clone();
this.listenTo(SeriesCollection, 'sync', this._renderView); this.listenTo(SeriesCollection, 'sync', function (model, collection, options) {
this.listenTo(SeriesCollection, 'remove', this._renderView); this.seriesCollection.shadowCollection.add(model, options);
this.seriesCollection.fullCollection.resetFiltered();
this._renderView();
});
this.listenTo(SeriesCollection, 'add', function (model, collection, options) {
this.seriesCollection.shadowCollection.add(model, options);
this.seriesCollection.fullCollection.resetFiltered();
this._renderView();
});
this.listenTo(SeriesCollection, 'remove', function (model, collection, options) {
this.seriesCollection.shadowCollection.remove(model, options);
this.seriesCollection.fullCollection.resetFiltered();
this._renderView();
});
this.sortingOptions = { this.sortingOptions = {
type : 'sorting', type : 'sorting',
@ -255,7 +272,7 @@ define(
}, },
_showList: function () { _showList: function () {
this.currentView = new ListCollectionView({ this.currentView = new ListCollectionView({
collection: this.seriesCollection collection: this.seriesCollection
}); });
@ -269,7 +286,7 @@ define(
this._renderView(); this._renderView();
}, },
_renderView: function () { _renderView: function () {
if (SeriesCollection.length === 0) { if (SeriesCollection.length === 0) {
@ -278,6 +295,8 @@ define(
this.toolbar2.close(); this.toolbar2.close();
} }
else { else {
this._resetFilter();
this.seriesRegion.show(this.currentView); this.seriesRegion.show(this.currentView);
this._showToolbar(); this._showToolbar();
@ -295,6 +314,18 @@ define(
this.seriesCollection.setFilterMode(mode); this.seriesCollection.setFilterMode(mode);
}, },
_resetFilter: function () {
var key = this.seriesCollection.state.filterKey;
var value = this.seriesCollection.state.filterValue;
this.seriesCollection.setFilter([ null, null ]);
this.seriesCollection.setFilter([ key, value ]);
},
_shadowTest: function () {
window.alert('added to shadow');
},
_showToolbar: function () { _showToolbar: function () {
if (this.toolbar.currentView) { if (this.toolbar.currentView) {

@ -8,7 +8,8 @@ define(
'api!series', 'api!series',
'Mixins/AsFilteredCollection', 'Mixins/AsFilteredCollection',
'Mixins/AsPersistedStateCollection', 'Mixins/AsPersistedStateCollection',
'moment' 'moment',
'Mixins/backbone.signalr.mixin'
], function (_, Backbone, PageableCollection, SeriesModel, SeriesData, AsFilteredCollection, AsPersistedStateCollection, Moment) { ], function (_, Backbone, PageableCollection, SeriesModel, SeriesData, AsFilteredCollection, AsPersistedStateCollection, Moment) {
var Collection = PageableCollection.extend({ var Collection = PageableCollection.extend({
url : window.NzbDrone.ApiRoot + '/series', url : window.NzbDrone.ApiRoot + '/series',
@ -72,5 +73,5 @@ define(
var MixedIn = AsPersistedStateCollection.call(FilteredCollection); var MixedIn = AsPersistedStateCollection.call(FilteredCollection);
var collection = new MixedIn(SeriesData, { full: true }); var collection = new MixedIn(SeriesData, { full: true });
return collection; return collection.bindSignalR();
}); });

@ -50,7 +50,6 @@ define(
throw 'ownerContext must be set.'; throw 'ownerContext must be set.';
} }
var callback = this.model.get('callback'); var callback = this.model.get('callback');
if (callback) { if (callback) {
callback.call(this.model.ownerContext, this); callback.call(this.model.ownerContext, this);

Loading…
Cancel
Save