mirror of
https://github.com/Sonarr/Sonarr.git
synced 2024-10-30 07:22:35 +01:00
Episode Activity added
New: Activity tab added to Episode Details
This commit is contained in:
parent
d0d65db4f2
commit
c0b7612053
16
Gruntfile.js
16
Gruntfile.js
@ -169,35 +169,35 @@ module.exports = function (grunt) {
|
|||||||
tasks: ['handlebars']
|
tasks: ['handlebars']
|
||||||
},
|
},
|
||||||
copyIndex : {
|
copyIndex : {
|
||||||
files: '<%= copy.index.src %>',
|
files: '<%= copy.index.cwd %><%= copy.index.src %>',
|
||||||
tasks: ['copy:index']
|
tasks: ['copy:index']
|
||||||
},
|
},
|
||||||
copyScripts: {
|
copyScripts: {
|
||||||
files: '<%= copy.scripts.src %>',
|
files: '<%= copy.scripts.cwd %><%= copy.scripts.src %>',
|
||||||
tasks: ['copy:scripts']
|
tasks: ['copy:scripts']
|
||||||
},
|
},
|
||||||
copyStyles : {
|
copyStyles : {
|
||||||
files: '<%= copy.styles.src %>',
|
files: '<%= copy.styles.cwd %><%= copy.styles.src %>',
|
||||||
tasks: ['copy:styles']
|
tasks: ['copy:styles']
|
||||||
},
|
},
|
||||||
copyImages : {
|
copyImages : {
|
||||||
files: '<%= copy.images.src %>',
|
files: '<%= copy.images.cwd %><%= copy.images.src %>',
|
||||||
tasks: ['copy:images']
|
tasks: ['copy:images']
|
||||||
},
|
},
|
||||||
copyJpg : {
|
copyJpg : {
|
||||||
files: '<%= copy.jpg.src %>',
|
files: '<%= copy.jpg.cwd %><%= copy.jpg.src %>',
|
||||||
tasks: ['copy:jpg']
|
tasks: ['copy:jpg']
|
||||||
},
|
},
|
||||||
copyIcon : {
|
copyIcon : {
|
||||||
files: '<%= copy.icon.src %>',
|
files: '<%= copy.icon.cwd %><%= copy.icon.src %>',
|
||||||
tasks: ['copy:icon']
|
tasks: ['copy:icon']
|
||||||
},
|
},
|
||||||
copyFontAwesome : {
|
copyFontAwesome : {
|
||||||
files: '<%= copy.fontAwesome.src %>',
|
files: '<%= copy.fontAwesome.cwd %><%= copy.fontAwesome.src %>',
|
||||||
tasks: ['copy:fontAwesome']
|
tasks: ['copy:fontAwesome']
|
||||||
},
|
},
|
||||||
copyFonts : {
|
copyFonts : {
|
||||||
files: '<%= copy.fonts.src %>',
|
files: '<%= copy.fonts.cwd %><%= copy.fonts.src %>',
|
||||||
tasks: ['copy:fonts']
|
tasks: ['copy:fonts']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
39
src/NzbDrone.Api/Episodes/EpisodeActivityModule.cs
Normal file
39
src/NzbDrone.Api/Episodes/EpisodeActivityModule.cs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using NzbDrone.Api.History;
|
||||||
|
using NzbDrone.Api.Mapping;
|
||||||
|
using NzbDrone.Api.REST;
|
||||||
|
using NzbDrone.Core.Datastore.Events;
|
||||||
|
using NzbDrone.Core.Download;
|
||||||
|
using NzbDrone.Core.History;
|
||||||
|
using NzbDrone.Core.MediaFiles.Events;
|
||||||
|
using NzbDrone.Core.Tv;
|
||||||
|
|
||||||
|
namespace NzbDrone.Api.Episodes
|
||||||
|
{
|
||||||
|
public class EpisodeActivityModule : NzbDroneRestModule<HistoryResource>
|
||||||
|
|
||||||
|
{
|
||||||
|
private readonly IHistoryService _historyService;
|
||||||
|
|
||||||
|
public EpisodeActivityModule(IHistoryService historyService)
|
||||||
|
: base("episodes/activity")
|
||||||
|
{
|
||||||
|
_historyService = historyService;
|
||||||
|
|
||||||
|
GetResourceAll = GetActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<HistoryResource> GetActivity()
|
||||||
|
{
|
||||||
|
var episodeId = (int?)Request.Query.EpisodeId;
|
||||||
|
|
||||||
|
if (episodeId == null)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("episodeId is missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ToListResource(() => _historyService.ByEpisode(episodeId.Value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -91,6 +91,7 @@
|
|||||||
<Compile Include="EpisodeFiles\EpisodeFileResource.cs" />
|
<Compile Include="EpisodeFiles\EpisodeFileResource.cs" />
|
||||||
<Compile Include="Directories\DirectoryLookupService.cs" />
|
<Compile Include="Directories\DirectoryLookupService.cs" />
|
||||||
<Compile Include="Directories\DirectoryModule.cs" />
|
<Compile Include="Directories\DirectoryModule.cs" />
|
||||||
|
<Compile Include="Episodes\EpisodeActivityModule.cs" />
|
||||||
<Compile Include="Episodes\EpisodeModule.cs" />
|
<Compile Include="Episodes\EpisodeModule.cs" />
|
||||||
<Compile Include="Episodes\EpisodeResource.cs" />
|
<Compile Include="Episodes\EpisodeResource.cs" />
|
||||||
<Compile Include="Extensions\Pipelines\CacheHeaderPipeline.cs" />
|
<Compile Include="Extensions\Pipelines\CacheHeaderPipeline.cs" />
|
||||||
|
@ -12,6 +12,7 @@ namespace NzbDrone.Core.History
|
|||||||
{
|
{
|
||||||
void Trim();
|
void Trim();
|
||||||
List<QualityModel> GetEpisodeHistory(int episodeId);
|
List<QualityModel> GetEpisodeHistory(int episodeId);
|
||||||
|
List<History> ByEpisode(int episodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class HistoryRepository : BasicRepository<History>, IHistoryRepository
|
public class HistoryRepository : BasicRepository<History>, IHistoryRepository
|
||||||
@ -37,6 +38,11 @@ namespace NzbDrone.Core.History
|
|||||||
return history.Select(h => h.Quality).ToList();
|
return history.Select(h => h.Quality).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<History> ByEpisode(int episodeId)
|
||||||
|
{
|
||||||
|
return Query.Where(h => h.EpisodeId == episodeId).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
public override PagingSpec<History> GetPaged(PagingSpec<History> pagingSpec)
|
public override PagingSpec<History> GetPaged(PagingSpec<History> pagingSpec)
|
||||||
{
|
{
|
||||||
var pagingQuery = Query.Join<History, Series>(JoinType.Inner, h => h.Series, (h, s) => h.SeriesId == s.Id)
|
var pagingQuery = Query.Join<History, Series>(JoinType.Inner, h => h.Series, (h, s) => h.SeriesId == s.Id)
|
||||||
|
@ -17,6 +17,7 @@ namespace NzbDrone.Core.History
|
|||||||
void Trim();
|
void Trim();
|
||||||
QualityModel GetBestQualityInHistory(int episodeId);
|
QualityModel GetBestQualityInHistory(int episodeId);
|
||||||
PagingSpec<History> Paged(PagingSpec<History> pagingSpec);
|
PagingSpec<History> Paged(PagingSpec<History> pagingSpec);
|
||||||
|
List<History> ByEpisode(int episodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class HistoryService : IHistoryService, IHandle<EpisodeGrabbedEvent>, IHandle<EpisodeImportedEvent>
|
public class HistoryService : IHistoryService, IHandle<EpisodeGrabbedEvent>, IHandle<EpisodeImportedEvent>
|
||||||
@ -40,6 +41,11 @@ namespace NzbDrone.Core.History
|
|||||||
return _historyRepository.GetPaged(pagingSpec);
|
return _historyRepository.GetPaged(pagingSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<History> ByEpisode(int episodeId)
|
||||||
|
{
|
||||||
|
return _historyRepository.ByEpisode(episodeId);
|
||||||
|
}
|
||||||
|
|
||||||
public void Purge()
|
public void Purge()
|
||||||
{
|
{
|
||||||
_historyRepository.Purge();
|
_historyRepository.Purge();
|
||||||
|
@ -35,7 +35,7 @@ define(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$el.html('<i class="{0}" title="{1}"/>'.format(icon, toolTip));
|
this.$el.html('<i class="{0}" title="{1}" data-placement="right"/>'.format(icon, toolTip));
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
31
src/UI/Episode/Activity/EpisodeActivityCollection.js
Normal file
31
src/UI/Episode/Activity/EpisodeActivityCollection.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
'use strict';
|
||||||
|
define(
|
||||||
|
[
|
||||||
|
'backbone',
|
||||||
|
'Episode/Activity/EpisodeActivityModel'
|
||||||
|
], function (Backbone, EpisodeActivityModel) {
|
||||||
|
return Backbone.Collection.extend({
|
||||||
|
url : window.NzbDrone.ApiRoot + '/episodes/activity',
|
||||||
|
model: EpisodeActivityModel,
|
||||||
|
|
||||||
|
originalFetch: Backbone.Collection.prototype.fetch,
|
||||||
|
|
||||||
|
initialize: function (options) {
|
||||||
|
this.episodeId = options.episodeId;
|
||||||
|
},
|
||||||
|
|
||||||
|
fetch: function (options) {
|
||||||
|
if (!this.episodeId) {
|
||||||
|
throw 'episodeId is required';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options) {
|
||||||
|
options = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
options.data = { episodeId: this.episodeId };
|
||||||
|
|
||||||
|
return this.originalFetch.call(this, options);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
72
src/UI/Episode/Activity/EpisodeActivityLayout.js
Normal file
72
src/UI/Episode/Activity/EpisodeActivityLayout.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
'use strict';
|
||||||
|
define(
|
||||||
|
[
|
||||||
|
'app',
|
||||||
|
'marionette',
|
||||||
|
'backgrid',
|
||||||
|
'Episode/Activity/EpisodeActivityCollection',
|
||||||
|
'Cells/EventTypeCell',
|
||||||
|
'Cells/QualityCell',
|
||||||
|
'Cells/RelativeDateCell',
|
||||||
|
'Shared/LoadingView'
|
||||||
|
], function (App, Marionette, Backgrid, EpisodeActivityCollection, EventTypeCell, QualityCell, RelativeDateCell, LoadingView) {
|
||||||
|
|
||||||
|
return Marionette.Layout.extend({
|
||||||
|
template: 'Episode/Activity/EpisodeActivityLayoutTemplate',
|
||||||
|
|
||||||
|
regions: {
|
||||||
|
activityTable: '.activity-table'
|
||||||
|
},
|
||||||
|
|
||||||
|
columns:
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name : 'eventType',
|
||||||
|
label : '',
|
||||||
|
cell : EventTypeCell,
|
||||||
|
cellValue: 'this'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'sourceTitle',
|
||||||
|
label: 'Source Title',
|
||||||
|
cell : 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'quality',
|
||||||
|
label: 'Quality',
|
||||||
|
cell : QualityCell
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'date',
|
||||||
|
label: 'Date',
|
||||||
|
cell : RelativeDateCell
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
initialize: function (options) {
|
||||||
|
this.model = options.model;
|
||||||
|
this.series = options.series;
|
||||||
|
|
||||||
|
this.collection = new EpisodeActivityCollection({ episodeId: this.model.id });
|
||||||
|
},
|
||||||
|
|
||||||
|
onShow: function () {
|
||||||
|
var self = this;
|
||||||
|
this.activityTable.show(new LoadingView());
|
||||||
|
|
||||||
|
var promise = this.collection.fetch();
|
||||||
|
|
||||||
|
promise.done(function () {
|
||||||
|
self._showTable();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_showTable: function () {
|
||||||
|
this.activityTable.show(new Backgrid.Grid({
|
||||||
|
collection: this.collection,
|
||||||
|
columns : this.columns,
|
||||||
|
className : 'table table-hover table-condensed'
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1 @@
|
|||||||
|
<div class="activity-table"></div>
|
9
src/UI/Episode/Activity/EpisodeActivityModel.js
Normal file
9
src/UI/Episode/Activity/EpisodeActivityModel.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
'use strict';
|
||||||
|
define(
|
||||||
|
[
|
||||||
|
'backbone'
|
||||||
|
], function (Backbone) {
|
||||||
|
return Backbone.Model.extend({
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
@ -4,8 +4,9 @@ define(
|
|||||||
'marionette',
|
'marionette',
|
||||||
'Episode/Summary/EpisodeSummaryLayout',
|
'Episode/Summary/EpisodeSummaryLayout',
|
||||||
'Episode/Search/EpisodeSearchLayout',
|
'Episode/Search/EpisodeSearchLayout',
|
||||||
|
'Episode/Activity/EpisodeActivityLayout',
|
||||||
'Series/SeriesCollection'
|
'Series/SeriesCollection'
|
||||||
], function (Marionette, SummaryLayout, SearchLayout, SeriesCollection) {
|
], function (Marionette, SummaryLayout, SearchLayout, EpisodeActivityLayout, SeriesCollection) {
|
||||||
|
|
||||||
return Marionette.Layout.extend({
|
return Marionette.Layout.extend({
|
||||||
template: 'Episode/EpisodeDetailsLayoutTemplate',
|
template: 'Episode/EpisodeDetailsLayoutTemplate',
|
||||||
@ -38,7 +39,7 @@ define(
|
|||||||
|
|
||||||
this.series = SeriesCollection.find({ id: this.model.get('seriesId') });
|
this.series = SeriesCollection.find({ id: this.model.get('seriesId') });
|
||||||
this.templateHelpers.series = this.series.toJSON();
|
this.templateHelpers.series = this.series.toJSON();
|
||||||
this.openingTab = options.openingTab || 'summary'
|
this.openingTab = options.openingTab || 'summary';
|
||||||
},
|
},
|
||||||
|
|
||||||
onShow: function () {
|
onShow: function () {
|
||||||
@ -71,6 +72,7 @@ define(
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.ui.activity.tab('show');
|
this.ui.activity.tab('show');
|
||||||
|
this.activity.show(new EpisodeActivityLayout({model: this.model, series: this.series}));
|
||||||
},
|
},
|
||||||
|
|
||||||
_showSearch: function (e) {
|
_showSearch: function (e) {
|
||||||
|
@ -15,12 +15,12 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<ul class="nav nav-tabs" id="myTab">
|
<ul class="nav nav-tabs" id="myTab">
|
||||||
<li><a href="#episode-summary" class="x-episode-summary">Summary</a></li>
|
<li><a href="#episode-summary" class="x-episode-summary">Summary</a></li>
|
||||||
<!-- <li><a href="#episode-activity" class="x-episode-activity">Activity</a></li>-->
|
<li><a href="#episode-activity" class="x-episode-activity">Activity</a></li>
|
||||||
<li><a href="#episode-search" class="x-episode-search">Search</a></li>
|
<li><a href="#episode-search" class="x-episode-search">Search</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div class="tab-pane" id="episode-summary"/>
|
<div class="tab-pane" id="episode-summary"/>
|
||||||
<!--<div class="tab-pane" id="episode-activity"/>-->
|
<div class="tab-pane" id="episode-activity"/>
|
||||||
<div class="tab-pane" id="episode-search"/>
|
<div class="tab-pane" id="episode-search"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -58,7 +58,7 @@ define(
|
|||||||
if (App.reqres.hasHandler(App.Reqres.GetEpisodeFileById)) {
|
if (App.reqres.hasHandler(App.Reqres.GetEpisodeFileById)) {
|
||||||
var episodeFile = App.request(App.Reqres.GetEpisodeFileById, episodeFileId);
|
var episodeFile = App.request(App.Reqres.GetEpisodeFileById, episodeFileId);
|
||||||
var episodeFileCollection = new EpisodeFileCollection(episodeFile, { seriesId: this.model.get('seriesId') });
|
var episodeFileCollection = new EpisodeFileCollection(episodeFile, { seriesId: this.model.get('seriesId') });
|
||||||
this._showTable(episodeFileCollection)
|
this._showTable(episodeFileCollection);
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
|
@ -4,7 +4,7 @@ define(
|
|||||||
'marionette',
|
'marionette',
|
||||||
'backgrid',
|
'backgrid',
|
||||||
'History/HistoryCollection',
|
'History/HistoryCollection',
|
||||||
'History/Table/EventTypeCell',
|
'Cells/EventTypeCell',
|
||||||
'Cells/SeriesTitleCell',
|
'Cells/SeriesTitleCell',
|
||||||
'Cells/EpisodeNumberCell',
|
'Cells/EpisodeNumberCell',
|
||||||
'Cells/EpisodeTitleCell',
|
'Cells/EpisodeTitleCell',
|
||||||
|
Loading…
Reference in New Issue
Block a user