From 66873b04d467af53de84e1145d95322c14449d6c Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Mon, 23 Jun 2014 15:54:24 -0700 Subject: [PATCH 01/11] New: Network (if known) is shown on add series results Fixed: Ended showing on add series results --- src/UI/AddSeries/SearchResultViewTemplate.html | 11 ++++++++--- src/UI/AddSeries/addSeries.less | 10 +++++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/UI/AddSeries/SearchResultViewTemplate.html b/src/UI/AddSeries/SearchResultViewTemplate.html index d87a7e126..9fa74a539 100644 --- a/src/UI/AddSeries/SearchResultViewTemplate.html +++ b/src/UI/AddSeries/SearchResultViewTemplate.html @@ -10,9 +10,14 @@

{{titleWithYear}} - {{#unless_eq status compare="continuing"}} - Ended - {{/unless_eq}} + + + {{network}} + {{#unless_eq status compare="continuing"}} + Ended + {{/unless_eq}} + +

diff --git a/src/UI/AddSeries/addSeries.less b/src/UI/AddSeries/addSeries.less index 01a3b19fd..d0fdf3579 100644 --- a/src/UI/AddSeries/addSeries.less +++ b/src/UI/AddSeries/addSeries.less @@ -40,9 +40,13 @@ padding-bottom : 20px; .series-title { - .label { - margin-left: 15px; - vertical-align: middle; + .labels { + margin-left : 10px; + + .label { + font-size : 12px; + vertical-align : middle; + } } .year { From f5d46ffcd28f75805f136aa340b66d2222d28f42 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Tue, 24 Jun 2014 16:52:07 -0700 Subject: [PATCH 02/11] Log file changes New: Update log files are available in the UI Fixed: UI error after clearing log files --- .../Pipelines/CacheHeaderPipeline.cs | 5 +- .../Frontend/Mappers/LogFileMapper.cs | 8 +-- .../Frontend/Mappers/UpdateLogFileMapper.cs | 32 +++++++++ src/NzbDrone.Api/Logs/LogFileModule.cs | 54 +++++--------- src/NzbDrone.Api/Logs/LogFileModuleBase.cs | 72 +++++++++++++++++++ src/NzbDrone.Api/Logs/LogFileResource.cs | 2 + src/NzbDrone.Api/Logs/UpdateLogFileModule.cs | 47 ++++++++++++ src/NzbDrone.Api/NzbDrone.Api.csproj | 3 + .../Commands/DeleteUpdateLogFilesCommand.cs | 15 ++++ .../Instrumentation/DeleteLogFilesService.cs | 17 +++-- src/NzbDrone.Core/NzbDrone.Core.csproj | 1 + src/UI/System/Logs/Files/ContentsModel.js | 7 +- src/UI/System/Logs/Files/DownloadLogCell.js | 7 +- src/UI/System/Logs/Files/FilenameCell.js | 2 +- src/UI/System/Logs/Files/LogFileLayout.js | 24 +++---- src/UI/System/Logs/Files/Row.js | 2 +- src/UI/System/Logs/LogsLayout.js | 38 +++++++--- src/UI/System/Logs/LogsLayoutTemplate.html | 6 +- .../System/Logs/Updates/LogFileCollection.js | 17 +++++ src/UI/System/Logs/Updates/LogFileModel.js | 8 +++ 20 files changed, 282 insertions(+), 85 deletions(-) create mode 100644 src/NzbDrone.Api/Frontend/Mappers/UpdateLogFileMapper.cs create mode 100644 src/NzbDrone.Api/Logs/LogFileModuleBase.cs create mode 100644 src/NzbDrone.Api/Logs/UpdateLogFileModule.cs create mode 100644 src/NzbDrone.Core/Instrumentation/Commands/DeleteUpdateLogFilesCommand.cs create mode 100644 src/UI/System/Logs/Updates/LogFileCollection.js create mode 100644 src/UI/System/Logs/Updates/LogFileModel.js diff --git a/src/NzbDrone.Api/Extensions/Pipelines/CacheHeaderPipeline.cs b/src/NzbDrone.Api/Extensions/Pipelines/CacheHeaderPipeline.cs index 1ac4ae86c..28cecd695 100644 --- a/src/NzbDrone.Api/Extensions/Pipelines/CacheHeaderPipeline.cs +++ b/src/NzbDrone.Api/Extensions/Pipelines/CacheHeaderPipeline.cs @@ -13,7 +13,10 @@ namespace NzbDrone.Api.Extensions.Pipelines private void Handle(NancyContext context) { - context.Response.Headers.Add("X-ApplicationVersion", BuildInfo.Version.ToString()); + if (!context.Response.Headers.ContainsKey("X-ApplicationVersion")) + { + context.Response.Headers.Add("X-ApplicationVersion", BuildInfo.Version.ToString()); + } } } } \ No newline at end of file diff --git a/src/NzbDrone.Api/Frontend/Mappers/LogFileMapper.cs b/src/NzbDrone.Api/Frontend/Mappers/LogFileMapper.cs index bc7124a97..588775839 100644 --- a/src/NzbDrone.Api/Frontend/Mappers/LogFileMapper.cs +++ b/src/NzbDrone.Api/Frontend/Mappers/LogFileMapper.cs @@ -6,11 +6,11 @@ using NzbDrone.Common.EnvironmentInfo; namespace NzbDrone.Api.Frontend.Mappers { - public class LogFileMapper : StaticResourceMapperBase + public class UpdateLogFileMapper : StaticResourceMapperBase { private readonly IAppFolderInfo _appFolderInfo; - public LogFileMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, Logger logger) + public UpdateLogFileMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, Logger logger) : base(diskProvider, logger) { _appFolderInfo = appFolderInfo; @@ -21,12 +21,12 @@ namespace NzbDrone.Api.Frontend.Mappers var path = resourceUrl.Replace('/', Path.DirectorySeparatorChar); path = Path.GetFileName(path); - return Path.Combine(_appFolderInfo.GetLogFolder(), path); + return Path.Combine(_appFolderInfo.GetUpdateLogFolder(), path); } public override bool CanHandle(string resourceUrl) { - return resourceUrl.StartsWith("/logfile/") && resourceUrl.EndsWith(".txt"); + return resourceUrl.StartsWith("/updatelogfile/") && resourceUrl.EndsWith(".txt"); } } } \ No newline at end of file diff --git a/src/NzbDrone.Api/Frontend/Mappers/UpdateLogFileMapper.cs b/src/NzbDrone.Api/Frontend/Mappers/UpdateLogFileMapper.cs new file mode 100644 index 000000000..bc7124a97 --- /dev/null +++ b/src/NzbDrone.Api/Frontend/Mappers/UpdateLogFileMapper.cs @@ -0,0 +1,32 @@ +using System.IO; +using NLog; +using NzbDrone.Common; +using NzbDrone.Common.Disk; +using NzbDrone.Common.EnvironmentInfo; + +namespace NzbDrone.Api.Frontend.Mappers +{ + public class LogFileMapper : StaticResourceMapperBase + { + private readonly IAppFolderInfo _appFolderInfo; + + public LogFileMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, Logger logger) + : base(diskProvider, logger) + { + _appFolderInfo = appFolderInfo; + } + + protected override string Map(string resourceUrl) + { + var path = resourceUrl.Replace('/', Path.DirectorySeparatorChar); + path = Path.GetFileName(path); + + return Path.Combine(_appFolderInfo.GetLogFolder(), path); + } + + public override bool CanHandle(string resourceUrl) + { + return resourceUrl.StartsWith("/logfile/") && resourceUrl.EndsWith(".txt"); + } + } +} \ No newline at end of file diff --git a/src/NzbDrone.Api/Logs/LogFileModule.cs b/src/NzbDrone.Api/Logs/LogFileModule.cs index b44e77b6d..aaa8797d3 100644 --- a/src/NzbDrone.Api/Logs/LogFileModule.cs +++ b/src/NzbDrone.Api/Logs/LogFileModule.cs @@ -1,63 +1,43 @@ using System.Collections.Generic; using System.IO; -using System.Linq; using NzbDrone.Common; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; -using Nancy; -using Nancy.Responses; +using NzbDrone.Core.Configuration; namespace NzbDrone.Api.Logs { - public class LogFileModule : NzbDroneRestModule + public class LogFileModule : LogFileModuleBase { - private const string LOGFILE_ROUTE = @"/(?nzbdrone(?:\.\d+)?\.txt)"; - private readonly IAppFolderInfo _appFolderInfo; private readonly IDiskProvider _diskProvider; public LogFileModule(IAppFolderInfo appFolderInfo, - IDiskProvider diskProvider) - : base("log/file") + IDiskProvider diskProvider, + IConfigFileProvider configFileProvider) + : base(diskProvider, configFileProvider, "") { _appFolderInfo = appFolderInfo; _diskProvider = diskProvider; - GetResourceAll = GetLogFiles; - - Get[LOGFILE_ROUTE] = options => GetLogFile(options.filename); } - private List GetLogFiles() + protected override IEnumerable GetLogFiles() { - var result = new List(); - - var files = _diskProvider.GetFiles(_appFolderInfo.GetLogFolder(), SearchOption.TopDirectoryOnly); + return _diskProvider.GetFiles(_appFolderInfo.GetLogFolder(), SearchOption.TopDirectoryOnly); + } - for (int i = 0; i < files.Length; i++) + protected override string GetLogFilePath(string filename) + { + return Path.Combine(_appFolderInfo.GetLogFolder(), filename); + } + + protected override string DownloadUrlRoot + { + get { - var file = files[i]; - - result.Add(new LogFileResource - { - Id = i + 1, - Filename = Path.GetFileName(file), - LastWriteTime = _diskProvider.FileGetLastWriteUtc(file) - }); + return "logfile"; } - - return result.OrderByDescending(l => l.LastWriteTime).ToList(); } - private Response GetLogFile(string filename) - { - var filePath = Path.Combine(_appFolderInfo.GetLogFolder(), filename); - - if (!_diskProvider.FileExists(filePath)) - return new NotFoundResponse(); - - var data = _diskProvider.ReadAllText(filePath); - - return new TextResponse(data); - } } } \ No newline at end of file diff --git a/src/NzbDrone.Api/Logs/LogFileModuleBase.cs b/src/NzbDrone.Api/Logs/LogFileModuleBase.cs new file mode 100644 index 000000000..1b68a7a82 --- /dev/null +++ b/src/NzbDrone.Api/Logs/LogFileModuleBase.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using NzbDrone.Common.Disk; +using Nancy; +using Nancy.Responses; +using NzbDrone.Core.Configuration; + +namespace NzbDrone.Api.Logs +{ + public abstract class LogFileModuleBase : NzbDroneRestModule + { + protected const string LOGFILE_ROUTE = @"/(?[-.a-zA-Z0-9]+?\.txt)"; + + private readonly IDiskProvider _diskProvider; + private readonly IConfigFileProvider _configFileProvider; + + public LogFileModuleBase(IDiskProvider diskProvider, + IConfigFileProvider configFileProvider, + String route) + : base("log/file" + route) + { + _diskProvider = diskProvider; + _configFileProvider = configFileProvider; + GetResourceAll = GetLogFilesResponse; + + Get[LOGFILE_ROUTE] = options => GetLogFileResponse(options.filename); + } + + private List GetLogFilesResponse() + { + var result = new List(); + + var files = GetLogFiles().ToList(); + + for (int i = 0; i < files.Count; i++) + { + var file = files[i]; + var filename = Path.GetFileName(file); + + result.Add(new LogFileResource + { + Id = i + 1, + Filename = filename, + LastWriteTime = _diskProvider.FileGetLastWriteUtc(file), + ContentsUrl = String.Format("/api{0}/{1}/{2}", _configFileProvider.UrlBase, Resource, filename), + DownloadUrl = String.Format("{0}/{1}/{2}", _configFileProvider.UrlBase, DownloadUrlRoot, filename) + }); + } + + return result.OrderByDescending(l => l.LastWriteTime).ToList(); + } + + private Response GetLogFileResponse(String filename) + { + var filePath = GetLogFilePath(filename); + + if (!_diskProvider.FileExists(filePath)) + return new NotFoundResponse(); + + var data = _diskProvider.ReadAllText(filePath); + + return new TextResponse(data); + } + + protected abstract IEnumerable GetLogFiles(); + protected abstract String GetLogFilePath(String filename); + + protected abstract String DownloadUrlRoot { get; } + } +} \ No newline at end of file diff --git a/src/NzbDrone.Api/Logs/LogFileResource.cs b/src/NzbDrone.Api/Logs/LogFileResource.cs index 9d2847e89..8109eeb07 100644 --- a/src/NzbDrone.Api/Logs/LogFileResource.cs +++ b/src/NzbDrone.Api/Logs/LogFileResource.cs @@ -7,5 +7,7 @@ namespace NzbDrone.Api.Logs { public String Filename { get; set; } public DateTime LastWriteTime { get; set; } + public String ContentsUrl { get; set; } + public String DownloadUrl { get; set; } } } diff --git a/src/NzbDrone.Api/Logs/UpdateLogFileModule.cs b/src/NzbDrone.Api/Logs/UpdateLogFileModule.cs new file mode 100644 index 000000000..3c91173ce --- /dev/null +++ b/src/NzbDrone.Api/Logs/UpdateLogFileModule.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using NzbDrone.Common; +using NzbDrone.Common.Disk; +using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Core.Configuration; + +namespace NzbDrone.Api.Logs +{ + public class UpdateLogFileModule : LogFileModuleBase + { + private readonly IAppFolderInfo _appFolderInfo; + private readonly IDiskProvider _diskProvider; + + public UpdateLogFileModule(IAppFolderInfo appFolderInfo, + IDiskProvider diskProvider, + IConfigFileProvider configFileProvider) + : base(diskProvider, configFileProvider, "/update") + { + _appFolderInfo = appFolderInfo; + _diskProvider = diskProvider; + } + + protected override IEnumerable GetLogFiles() + { + return _diskProvider.GetFiles(_appFolderInfo.GetUpdateLogFolder(), SearchOption.TopDirectoryOnly) + .Where(f => Regex.IsMatch(Path.GetFileName(f), LOGFILE_ROUTE.TrimStart('/'), RegexOptions.IgnoreCase)) + .ToList(); + } + + protected override String GetLogFilePath(String filename) + { + return Path.Combine(_appFolderInfo.GetUpdateLogFolder(), filename); + } + + protected override String DownloadUrlRoot + { + get + { + return "updatelogfile"; + } + } + } +} \ No newline at end of file diff --git a/src/NzbDrone.Api/NzbDrone.Api.csproj b/src/NzbDrone.Api/NzbDrone.Api.csproj index 0c23ee731..fa1038d38 100644 --- a/src/NzbDrone.Api/NzbDrone.Api.csproj +++ b/src/NzbDrone.Api/NzbDrone.Api.csproj @@ -129,6 +129,7 @@ + @@ -141,6 +142,8 @@ + + diff --git a/src/NzbDrone.Core/Instrumentation/Commands/DeleteUpdateLogFilesCommand.cs b/src/NzbDrone.Core/Instrumentation/Commands/DeleteUpdateLogFilesCommand.cs new file mode 100644 index 000000000..a55e91502 --- /dev/null +++ b/src/NzbDrone.Core/Instrumentation/Commands/DeleteUpdateLogFilesCommand.cs @@ -0,0 +1,15 @@ +using NzbDrone.Core.Messaging.Commands; + +namespace NzbDrone.Core.Instrumentation.Commands +{ + public class DeleteUpdateLogFilesCommand : Command + { + public override bool SendUpdatesToClient + { + get + { + return true; + } + } + } +} \ No newline at end of file diff --git a/src/NzbDrone.Core/Instrumentation/DeleteLogFilesService.cs b/src/NzbDrone.Core/Instrumentation/DeleteLogFilesService.cs index 25ae79cad..e2e31ca7b 100644 --- a/src/NzbDrone.Core/Instrumentation/DeleteLogFilesService.cs +++ b/src/NzbDrone.Core/Instrumentation/DeleteLogFilesService.cs @@ -1,4 +1,5 @@ -using System.IO; +using System; +using System.IO; using NLog; using NzbDrone.Common; using NzbDrone.Common.Disk; @@ -12,7 +13,7 @@ namespace NzbDrone.Core.Instrumentation { } - public class DeleteLogFilesService : IDeleteLogFilesService, IExecute + public class DeleteLogFilesService : IDeleteLogFilesService, IExecute, IExecute { private readonly IDiskProvider _diskProvider; private readonly IAppFolderInfo _appFolderInfo; @@ -27,12 +28,14 @@ namespace NzbDrone.Core.Instrumentation public void Execute(DeleteLogFilesCommand message) { - var logFiles = _diskProvider.GetFiles(_appFolderInfo.GetLogFolder(), SearchOption.TopDirectoryOnly); + _logger.Debug("Deleting all files in: {0}", _appFolderInfo.GetLogFolder()); + _diskProvider.EmptyFolder(_appFolderInfo.GetLogFolder()); + } - foreach (var logFile in logFiles) - { - _diskProvider.DeleteFile(logFile); - } + public void Execute(DeleteUpdateLogFilesCommand message) + { + _logger.Debug("Deleting all files in: {0}", _appFolderInfo.GetUpdateLogFolder()); + _diskProvider.EmptyFolder(_appFolderInfo.GetUpdateLogFolder()); } } } diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index 8b70b8ca6..97f8093a6 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -334,6 +334,7 @@ + diff --git a/src/UI/System/Logs/Files/ContentsModel.js b/src/UI/System/Logs/Files/ContentsModel.js index d109013dc..baf529561 100644 --- a/src/UI/System/Logs/Files/ContentsModel.js +++ b/src/UI/System/Logs/Files/ContentsModel.js @@ -1,12 +1,11 @@ 'use strict'; define( [ - 'backbone', - 'System/StatusModel' - ], function (Backbone, StatusModel) { + 'backbone' + ], function (Backbone) { return Backbone.Model.extend({ url: function () { - return StatusModel.get('urlBase') + '/api/log/file/' + this.get('filename'); + return this.get('contentsUrl'); }, parse: function (contents) { diff --git a/src/UI/System/Logs/Files/DownloadLogCell.js b/src/UI/System/Logs/Files/DownloadLogCell.js index 6ce544374..63dfb66f2 100644 --- a/src/UI/System/Logs/Files/DownloadLogCell.js +++ b/src/UI/System/Logs/Files/DownloadLogCell.js @@ -1,16 +1,15 @@ 'use strict'; define( [ - 'Cells/NzbDroneCell', - 'System/StatusModel' - ], function (NzbDroneCell, StatusModel) { + '../../../Cells/NzbDroneCell' + ], function (NzbDroneCell) { return NzbDroneCell.extend({ className: 'download-log-cell', render: function () { this.$el.empty(); - this.$el.html('Download'.format(StatusModel.get('urlBase'), this.cellValue)); + this.$el.html('Download'.format(this.cellValue)); return this; } diff --git a/src/UI/System/Logs/Files/FilenameCell.js b/src/UI/System/Logs/Files/FilenameCell.js index 3fefb67a5..feec6ed8b 100644 --- a/src/UI/System/Logs/Files/FilenameCell.js +++ b/src/UI/System/Logs/Files/FilenameCell.js @@ -1,7 +1,7 @@ 'use strict'; define( [ - 'Cells/NzbDroneCell' + '../../../Cells/NzbDroneCell' ], function (NzbDroneCell) { return NzbDroneCell.extend({ diff --git a/src/UI/System/Logs/Files/LogFileLayout.js b/src/UI/System/Logs/Files/LogFileLayout.js index 8cffc029e..75280515a 100644 --- a/src/UI/System/Logs/Files/LogFileLayout.js +++ b/src/UI/System/Logs/Files/LogFileLayout.js @@ -7,7 +7,6 @@ define( 'System/Logs/Files/FilenameCell', 'Cells/RelativeDateCell', 'System/Logs/Files/DownloadLogCell', - 'System/Logs/Files/LogFileCollection', 'System/Logs/Files/Row', 'System/Logs/Files/ContentsView', 'System/Logs/Files/ContentsModel', @@ -20,7 +19,6 @@ define( FilenameCell, RelativeDateCell, DownloadLogCell, - LogFileCollection, LogFileRow, ContentsView, ContentsModel, @@ -48,18 +46,19 @@ define( cell : RelativeDateCell }, { - name : 'filename', + name : 'downloadUrl', label : '', cell : DownloadLogCell, sortable: false } ], - initialize: function () { - this.collection = new LogFileCollection(); + initialize: function (options) { + this.collection = options.collection; + this.deleteFilesCommand = options.deleteFilesCommand; - vent.on(vent.Commands.ShowLogFile, this._fetchLogFileContents, this); - vent.on(vent.Events.CommandComplete, this._commandComplete, this); + this.listenTo(vent, vent.Commands.ShowLogFile, this._fetchLogFileContents); + this.listenTo(vent, vent.Events.CommandComplete, this._commandComplete); this.listenTo(this.collection, 'sync', this._collectionSynced); this.collection.fetch(); @@ -83,11 +82,10 @@ define( ownerContext : this, callback : this._refreshTable }, - { title : 'Delete Log Files', icon : 'icon-trash', - command : 'deleteLogFiles', + command : this.deleteFilesCommand, successMessage : 'Log files have been deleted', errorMessage : 'Failed to delete log files' } @@ -125,11 +123,7 @@ define( this.contents.show(new LoadingView()); var model = options.model; - var filename = model.get('filename'); - - var contentsModel = new ContentsModel({ - filename: filename - }); + var contentsModel = new ContentsModel(model.toJSON()); this.listenToOnce(contentsModel, 'sync', this._showDetails); @@ -151,7 +145,7 @@ define( }, _commandComplete: function (options) { - if (options.command.get('name') === 'deletelogfiles') { + if (options.command.get('name') === this.deleteFilesCommand.toLowerCase()) { this._refreshTable(); } } diff --git a/src/UI/System/Logs/Files/Row.js b/src/UI/System/Logs/Files/Row.js index 926869008..b506e214d 100644 --- a/src/UI/System/Logs/Files/Row.js +++ b/src/UI/System/Logs/Files/Row.js @@ -1,7 +1,7 @@ 'use strict'; define( [ - 'vent', + '../../../vent', 'backgrid' ], function (vent, Backgrid) { diff --git a/src/UI/System/Logs/LogsLayout.js b/src/UI/System/Logs/LogsLayout.js index 15511b431..a8c1cf0ac 100644 --- a/src/UI/System/Logs/LogsLayout.js +++ b/src/UI/System/Logs/LogsLayout.js @@ -3,24 +3,29 @@ define( [ 'marionette', 'System/Logs/Table/LogsTableLayout', - 'System/Logs/Files/LogFileLayout' - ], function (Marionette, LogsTableLayout, LogsFileLayout) { + 'System/Logs/Files/LogFileLayout', + 'System/Logs/Files/LogFileCollection', + 'System/Logs/Updates/LogFileCollection' + ], function (Marionette, LogsTableLayout, LogsFileLayout, LogFileCollection, UpdateLogFileCollection) { return Marionette.Layout.extend({ template: 'System/Logs/LogsLayoutTemplate', ui: { - tableTab: '.x-table-tab', - filesTab: '.x-files-tab' + tableTab : '.x-table-tab', + filesTab : '.x-files-tab', + updateFilesTab : '.x-update-files-tab' }, regions: { - table: '#table', - files: '#files' + table : '#table', + files : '#files', + updateFiles : '#update-files' }, events: { - 'click .x-table-tab': '_showTable', - 'click .x-files-tab': '_showFiles' + 'click .x-table-tab' : '_showTable', + 'click .x-files-tab' : '_showFiles', + 'click .x-update-files-tab' : '_showUpdateFiles' }, onShow: function () { @@ -42,7 +47,22 @@ define( } this.ui.filesTab.tab('show'); - this.files.show(new LogsFileLayout()); + this.files.show(new LogsFileLayout({ + collection: new LogFileCollection(), + deleteFilesCommand: 'deleteLogFiles' + })); + }, + + _showUpdateFiles: function (e) { + if (e) { + e.preventDefault(); + } + + this.ui.updateFilesTab.tab('show'); + this.updateFiles.show(new LogsFileLayout({ + collection: new UpdateLogFileCollection(), + deleteFilesCommand: 'deleteUpdateLogFiles' + })); } }); }); diff --git a/src/UI/System/Logs/LogsLayoutTemplate.html b/src/UI/System/Logs/LogsLayoutTemplate.html index aa250b933..a9832519a 100644 --- a/src/UI/System/Logs/LogsLayoutTemplate.html +++ b/src/UI/System/Logs/LogsLayoutTemplate.html @@ -1,15 +1,17 @@ 
-
+ -
+
+
\ No newline at end of file diff --git a/src/UI/System/Logs/Updates/LogFileCollection.js b/src/UI/System/Logs/Updates/LogFileCollection.js new file mode 100644 index 000000000..9a6d928f9 --- /dev/null +++ b/src/UI/System/Logs/Updates/LogFileCollection.js @@ -0,0 +1,17 @@ +'use strict'; + +define( + [ + 'backbone', + 'System/Logs/Updates/LogFileModel' + ], function (Backbone, LogFileModel) { + return Backbone.Collection.extend({ + url : window.NzbDrone.ApiRoot + '/log/file/update', + model: LogFileModel, + + state: { + sortKey: 'lastWriteTime', + order : 1 + } + }); + }); diff --git a/src/UI/System/Logs/Updates/LogFileModel.js b/src/UI/System/Logs/Updates/LogFileModel.js new file mode 100644 index 000000000..17c0fd6da --- /dev/null +++ b/src/UI/System/Logs/Updates/LogFileModel.js @@ -0,0 +1,8 @@ +'use strict'; +define( + [ + 'backbone' + ], function (Backbone) { + return Backbone.Model.extend({ + }); + }); From 3735736bbccd7497fec629b28888e82a8c255942 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Tue, 24 Jun 2014 16:55:21 -0700 Subject: [PATCH 03/11] Replaced vent.on with this.listenTo vent --- src/UI/History/Blacklist/BlacklistLayout.js | 3 ++- src/UI/Series/Details/SeriesDetailsLayout.js | 3 +-- src/UI/Settings/General/GeneralView.js | 2 +- src/UI/System/Logs/Table/LogsTableLayout.js | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/UI/History/Blacklist/BlacklistLayout.js b/src/UI/History/Blacklist/BlacklistLayout.js index 8a6a74487..d8b214ca2 100644 --- a/src/UI/History/Blacklist/BlacklistLayout.js +++ b/src/UI/History/Blacklist/BlacklistLayout.js @@ -67,8 +67,9 @@ define( initialize: function () { this.collection = new BlacklistCollection({ tableName: 'blacklist' }); + this.listenTo(this.collection, 'sync', this._showTable); - vent.on(vent.Events.CommandComplete, this._commandComplete, this); + this.listenTo(vent, vent.Events.CommandComplete, this._commandComplete); }, onShow: function () { diff --git a/src/UI/Series/Details/SeriesDetailsLayout.js b/src/UI/Series/Details/SeriesDetailsLayout.js index 95d20c627..cceef6655 100644 --- a/src/UI/Series/Details/SeriesDetailsLayout.js +++ b/src/UI/Series/Details/SeriesDetailsLayout.js @@ -59,8 +59,7 @@ define( initialize: function () { this.listenTo(this.model, 'change:monitored', this._setMonitoredState); this.listenTo(vent, vent.Events.SeriesDeleted, this._onSeriesDeleted); - - vent.on(vent.Events.CommandComplete, this._commandComplete, this); + this.listenTo(vent, vent.Events.CommandComplete, this._commandComplete); }, onShow: function () { diff --git a/src/UI/Settings/General/GeneralView.js b/src/UI/Settings/General/GeneralView.js index b28ed659c..1b5aaaed7 100644 --- a/src/UI/Settings/General/GeneralView.js +++ b/src/UI/Settings/General/GeneralView.js @@ -31,7 +31,7 @@ define( }, initialize: function () { - vent.on(vent.Events.CommandComplete, this._commandComplete, this); + this.listenTo(vent, vent.Events.CommandComplete, this._commandComplete); }, onRender: function(){ diff --git a/src/UI/System/Logs/Table/LogsTableLayout.js b/src/UI/System/Logs/Table/LogsTableLayout.js index 841a5a92b..2e06e1b7d 100644 --- a/src/UI/System/Logs/Table/LogsTableLayout.js +++ b/src/UI/System/Logs/Table/LogsTableLayout.js @@ -61,7 +61,7 @@ define( this.collection = new LogCollection(); this.listenTo(this.collection, 'sync', this._showTable); - vent.on(vent.Events.CommandComplete, this._commandComplete, this); + this.listenTo(vent, vent.Events.CommandComplete, this._commandComplete); }, onRender: function () { From 6a9993fa70afcb37a010a06270ef0be9fa9b51fe Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Tue, 24 Jun 2014 23:32:26 -0700 Subject: [PATCH 04/11] Fixed: Category is not required SABnzbd or NZBGet --- src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs | 8 +++----- src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs b/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs index e99382894..6dd047f4c 100644 --- a/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs +++ b/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs @@ -42,9 +42,8 @@ namespace NzbDrone.Core.Download.Clients.Nzbget { var url = remoteEpisode.Release.DownloadUrl; var title = remoteEpisode.Release.Title + ".nzb"; - - string category = Settings.TvCategory; - int priority = remoteEpisode.IsRecentEpisode() ? Settings.RecentTvPriority : Settings.OlderTvPriority; + var category = Settings.TvCategory; + var priority = remoteEpisode.IsRecentEpisode() ? Settings.RecentTvPriority : Settings.OlderTvPriority; _logger.Info("Adding report [{0}] to the queue.", title); @@ -271,10 +270,9 @@ namespace NzbDrone.Core.Download.Clients.Nzbget _proxy.GetVersion(settings); var config = _proxy.GetConfig(settings); - var categories = GetCategories(config); - if (!categories.Any(v => v.Name == settings.TvCategory)) + if (!settings.TvCategory.IsNullOrWhiteSpace() && !categories.Any(v => v.Name == settings.TvCategory)) { throw new ApplicationException("Category does not exist"); } diff --git a/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs b/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs index e14c15336..5c5a05e31 100644 --- a/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs +++ b/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs @@ -222,7 +222,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd { var categories = _proxy.GetCategories(settings); - if (!categories.Any(v => v == settings.TvCategory)) + if (!settings.TvCategory.IsNullOrWhiteSpace() && !categories.Any(v => v == settings.TvCategory)) { throw new ApplicationException("Category does not exist"); } From 83207a0bafa871fe4a1d50edef50a0e5636bfa85 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Wed, 25 Jun 2014 00:09:16 -0700 Subject: [PATCH 05/11] listenTo instead of on --- .../MediaFiles/RenameEpisodeFileService.cs | 12 ++++-------- src/UI/AddSeries/AddSeriesLayout.js | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/NzbDrone.Core/MediaFiles/RenameEpisodeFileService.cs b/src/NzbDrone.Core/MediaFiles/RenameEpisodeFileService.cs index 060f3c3ba..6a81ee4c8 100644 --- a/src/NzbDrone.Core/MediaFiles/RenameEpisodeFileService.cs +++ b/src/NzbDrone.Core/MediaFiles/RenameEpisodeFileService.cs @@ -73,8 +73,9 @@ namespace NzbDrone.Core.MediaFiles private IEnumerable GetPreviews(Series series, List episodes, List files) { - foreach (var file in files) + foreach (var f in files) { + var file = f; var episodesInFile = episodes.Where(e => e.EpisodeFileId == file.Id).ToList(); if (!episodesInFile.Any()) @@ -95,18 +96,13 @@ namespace NzbDrone.Core.MediaFiles SeasonNumber = seasonNumber, EpisodeNumbers = episodesInFile.Select(e => e.EpisodeNumber).ToList(), EpisodeFileId = file.Id, - ExistingPath = GetRelativePath(series.Path, file.Path), - NewPath = GetRelativePath(series.Path, newPath) + ExistingPath = series.Path.GetRelativePath(file.Path), + NewPath = series.Path.GetRelativePath(newPath) }; } } } - private string GetRelativePath(string seriesPath, string path) - { - return path.Substring(seriesPath.Length + 1); - } - private void RenameFiles(List episodeFiles, Series series) { var renamed = new List(); diff --git a/src/UI/AddSeries/AddSeriesLayout.js b/src/UI/AddSeries/AddSeriesLayout.js index e6e265abe..b51245d3a 100644 --- a/src/UI/AddSeries/AddSeriesLayout.js +++ b/src/UI/AddSeries/AddSeriesLayout.js @@ -55,7 +55,7 @@ define( _importSeries: function () { this.rootFolderLayout = new RootFolderLayout(); - this.rootFolderLayout.on('folderSelected', this._folderSelected, this); + this.listenTo(this.rootFolderLayout, 'folderSelected', this._folderSelected); AppLayout.modalRegion.show(this.rootFolderLayout); }, From cb291d30b39ddeb3a640be9dbf2087d619a6edf7 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Wed, 25 Jun 2014 01:31:37 -0700 Subject: [PATCH 06/11] New: Missing/Cutoff Unmet will update when an episode is grabbed --- src/NzbDrone.Api/Calendar/CalendarModule.cs | 27 +---------- src/NzbDrone.Api/Episodes/EpisodeModule.cs | 5 +- .../Episodes/EpisodeModuleWithSignalR.cs | 46 +++++++++++++++++++ src/NzbDrone.Api/NzbDrone.Api.csproj | 1 + src/NzbDrone.Api/Wanted/CutoffModule.cs | 9 ++-- src/NzbDrone.Api/Wanted/MissingModule.cs | 9 ++-- src/UI/Wanted/Cutoff/CutoffUnmetLayout.js | 5 +- src/UI/Wanted/Missing/MissingLayout.js | 5 +- 8 files changed, 65 insertions(+), 42 deletions(-) create mode 100644 src/NzbDrone.Api/Episodes/EpisodeModuleWithSignalR.cs diff --git a/src/NzbDrone.Api/Calendar/CalendarModule.cs b/src/NzbDrone.Api/Calendar/CalendarModule.cs index 484d3a52b..372515e64 100644 --- a/src/NzbDrone.Api/Calendar/CalendarModule.cs +++ b/src/NzbDrone.Api/Calendar/CalendarModule.cs @@ -4,18 +4,12 @@ using System.Linq; using NzbDrone.Api.Episodes; using NzbDrone.Api.Extensions; using NzbDrone.Api.Mapping; -using NzbDrone.Core.Datastore.Events; -using NzbDrone.Core.Download; -using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.Messaging.Commands; -using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Tv; namespace NzbDrone.Api.Calendar { - public class CalendarModule : NzbDroneRestModuleWithSignalR, - IHandle, - IHandle + public class CalendarModule : EpisodeModuleWithSignalR { private readonly IEpisodeService _episodeService; private readonly SeriesRepository _seriesRepository; @@ -53,24 +47,5 @@ namespace NzbDrone.Api.Calendar return resources.OrderBy(e => e.AirDateUtc).ToList(); } - - public void Handle(EpisodeGrabbedEvent message) - { - foreach (var episode in message.Episode.Episodes) - { - var resource = episode.InjectTo(); - resource.Grabbed = true; - - BroadcastResourceChange(ModelAction.Updated, resource); - } - } - - public void Handle(EpisodeDownloadedEvent message) - { - foreach (var episode in message.Episode.Episodes) - { - BroadcastResourceChange(ModelAction.Updated, episode.Id); - } - } } } diff --git a/src/NzbDrone.Api/Episodes/EpisodeModule.cs b/src/NzbDrone.Api/Episodes/EpisodeModule.cs index 4e24cb78f..0fc9fb59b 100644 --- a/src/NzbDrone.Api/Episodes/EpisodeModule.cs +++ b/src/NzbDrone.Api/Episodes/EpisodeModule.cs @@ -5,14 +5,11 @@ using NzbDrone.Core.Datastore.Events; using NzbDrone.Core.Download; using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.Messaging.Commands; -using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Tv; namespace NzbDrone.Api.Episodes { - public class EpisodeModule : NzbDroneRestModuleWithSignalR, - IHandle, - IHandle + public class EpisodeModule : EpisodeModuleWithSignalR { private readonly IEpisodeService _episodeService; diff --git a/src/NzbDrone.Api/Episodes/EpisodeModuleWithSignalR.cs b/src/NzbDrone.Api/Episodes/EpisodeModuleWithSignalR.cs new file mode 100644 index 000000000..8a305eb1b --- /dev/null +++ b/src/NzbDrone.Api/Episodes/EpisodeModuleWithSignalR.cs @@ -0,0 +1,46 @@ +using NzbDrone.Api.Mapping; +using NzbDrone.Core.Datastore.Events; +using NzbDrone.Core.Download; +using NzbDrone.Core.MediaFiles.Events; +using NzbDrone.Core.Messaging.Commands; +using NzbDrone.Core.Messaging.Events; +using NzbDrone.Core.Tv; + +namespace NzbDrone.Api.Episodes +{ + public abstract class EpisodeModuleWithSignalR : NzbDroneRestModuleWithSignalR, + IHandle, + IHandle + where TResource : EpisodeResource, new() + where TModel : Episode + { + protected EpisodeModuleWithSignalR(ICommandExecutor commandExecutor) + : base(commandExecutor) + { + } + + protected EpisodeModuleWithSignalR(ICommandExecutor commandExecutor, string resource) + : base(commandExecutor, resource) + { + } + + public void Handle(EpisodeGrabbedEvent message) + { + foreach (var episode in message.Episode.Episodes) + { + var resource = episode.InjectTo(); + resource.Grabbed = true; + + BroadcastResourceChange(ModelAction.Updated, resource); + } + } + + public void Handle(EpisodeDownloadedEvent message) + { + foreach (var episode in message.Episode.Episodes) + { + BroadcastResourceChange(ModelAction.Updated, episode.Id); + } + } + } +} \ No newline at end of file diff --git a/src/NzbDrone.Api/NzbDrone.Api.csproj b/src/NzbDrone.Api/NzbDrone.Api.csproj index fa1038d38..21a84886e 100644 --- a/src/NzbDrone.Api/NzbDrone.Api.csproj +++ b/src/NzbDrone.Api/NzbDrone.Api.csproj @@ -117,6 +117,7 @@ + diff --git a/src/NzbDrone.Api/Wanted/CutoffModule.cs b/src/NzbDrone.Api/Wanted/CutoffModule.cs index ee0b9f219..a9ab765e2 100644 --- a/src/NzbDrone.Api/Wanted/CutoffModule.cs +++ b/src/NzbDrone.Api/Wanted/CutoffModule.cs @@ -2,17 +2,18 @@ using NzbDrone.Api.Episodes; using NzbDrone.Api.Extensions; using NzbDrone.Core.Datastore; +using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Tv; namespace NzbDrone.Api.Wanted { - public class CutoffModule : NzbDroneRestModule + public class CutoffModule : EpisodeModuleWithSignalR { private readonly IEpisodeCutoffService _episodeCutoffService; - private readonly SeriesRepository _seriesRepository; + private readonly ISeriesRepository _seriesRepository; - public CutoffModule(IEpisodeCutoffService episodeCutoffService, SeriesRepository seriesRepository) - :base("wanted/cutoff") + public CutoffModule(IEpisodeCutoffService episodeCutoffService, ISeriesRepository seriesRepository, ICommandExecutor commandExecutor) + :base(commandExecutor, "wanted/cutoff") { _episodeCutoffService = episodeCutoffService; _seriesRepository = seriesRepository; diff --git a/src/NzbDrone.Api/Wanted/MissingModule.cs b/src/NzbDrone.Api/Wanted/MissingModule.cs index dd4d97f69..81b8d17cf 100644 --- a/src/NzbDrone.Api/Wanted/MissingModule.cs +++ b/src/NzbDrone.Api/Wanted/MissingModule.cs @@ -2,17 +2,18 @@ using NzbDrone.Api.Episodes; using NzbDrone.Api.Extensions; using NzbDrone.Core.Datastore; +using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Tv; namespace NzbDrone.Api.Wanted { - public class MissingModule : NzbDroneRestModule + public class MissingModule : EpisodeModuleWithSignalR { private readonly IEpisodeService _episodeService; - private readonly SeriesRepository _seriesRepository; + private readonly ISeriesRepository _seriesRepository; - public MissingModule(IEpisodeService episodeService, SeriesRepository seriesRepository) - :base("wanted/missing") + public MissingModule(IEpisodeService episodeService, ISeriesRepository seriesRepository, ICommandExecutor commandExecutor) + :base(commandExecutor, "wanted/missing") { _episodeService = episodeService; _seriesRepository = seriesRepository; diff --git a/src/UI/Wanted/Cutoff/CutoffUnmetLayout.js b/src/UI/Wanted/Cutoff/CutoffUnmetLayout.js index d01397bd4..df889f4f5 100644 --- a/src/UI/Wanted/Cutoff/CutoffUnmetLayout.js +++ b/src/UI/Wanted/Cutoff/CutoffUnmetLayout.js @@ -14,7 +14,8 @@ define([ 'Shared/LoadingView', 'Shared/Messenger', 'Commands/CommandController', - 'backgrid.selectall' + 'backgrid.selectall', + 'Mixins/backbone.signalr.mixin' ], function (_, Marionette, Backgrid, @@ -81,7 +82,7 @@ define([ ], initialize : function () { - this.collection = new CutoffUnmetCollection(); + this.collection = new CutoffUnmetCollection().bindSignalR({ updateOnly: true }); this.listenTo(this.collection, 'sync', this._showTable); }, diff --git a/src/UI/Wanted/Missing/MissingLayout.js b/src/UI/Wanted/Missing/MissingLayout.js index 45bfb9d4e..7e1cf6ae2 100644 --- a/src/UI/Wanted/Missing/MissingLayout.js +++ b/src/UI/Wanted/Missing/MissingLayout.js @@ -14,7 +14,8 @@ define([ 'Shared/LoadingView', 'Shared/Messenger', 'Commands/CommandController', - 'backgrid.selectall' + 'backgrid.selectall', + 'Mixins/backbone.signalr.mixin' ], function (_, Marionette, Backgrid, @@ -81,7 +82,7 @@ define([ ], initialize : function () { - this.collection = new MissingCollection(); + this.collection = new MissingCollection().bindSignalR({ updateOnly: true }); this.listenTo(this.collection, 'sync', this._showTable); }, From 5dcc6a74eb8014182df3975a4d1ed52be715710a Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Thu, 26 Jun 2014 22:45:21 -0700 Subject: [PATCH 07/11] iCal fixes Fixed: Calendar feed modal New: iCal URL now contains the API Key --- src/UI/Calendar/CalendarFeedView.js | 2 +- src/UI/Calendar/CalendarFeedViewTemplate.html | 29 +++++++++++-------- src/UI/Calendar/calendar.less | 3 +- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/UI/Calendar/CalendarFeedView.js b/src/UI/Calendar/CalendarFeedView.js index e69895af9..cf18cb711 100644 --- a/src/UI/Calendar/CalendarFeedView.js +++ b/src/UI/Calendar/CalendarFeedView.js @@ -14,7 +14,7 @@ define( }, templateHelpers: { - icalHttpUrl : window.location.protocol + '//' + window.location.host + StatusModel.get('urlBase') + '/feed/calendar/NzbDrone.ics', + icalHttpUrl : window.location.protocol + '//' + window.location.host + StatusModel.get('urlBase') + '/feed/calendar/NzbDrone.ics?apikey=' + window.NzbDrone.ApiKey, icalWebCalUrl : 'webcal://' + window.location.host + StatusModel.get('urlBase') + '/feed/calendar/NzbDrone.ics' }, diff --git a/src/UI/Calendar/CalendarFeedViewTemplate.html b/src/UI/Calendar/CalendarFeedViewTemplate.html index d920089b3..56ba9eb61 100644 --- a/src/UI/Calendar/CalendarFeedViewTemplate.html +++ b/src/UI/Calendar/CalendarFeedViewTemplate.html @@ -6,21 +6,26 @@