New: Series lists will auto update when files are imported/deleted
This commit is contained in:
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',
|
||||||
|
@ -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…
Reference in New Issue