mirror of
				https://github.com/Kareadita/Kavita.git
				synced 2025-10-31 10:37:04 -04:00 
			
		
		
		
	Bugfix/release cleanup (#512)
* Lots of cleanup on the warnings in the solution. Deprecated IsLastWriteLessThan and made a new method HasFileBeenModifiedSince. * Added some tests for the new extension method. * Changed filter import to use correct import * Scan Series now uses Refresh Metadata for Series, rather than library one. * Fixed an issue where cover generation wasn't properly taking forced update into consideration. Removed a case of cover generation for no reason. * Fixed series downloads not triggering backend call
This commit is contained in:
		
							parent
							
								
									2a3a08de74
								
							
						
					
					
						commit
						0d2d73e8ae
					
				| @ -1,21 +1,33 @@ | |||||||
| namespace API.Tests.Extensions | using System; | ||||||
|  | using System.Globalization; | ||||||
|  | using System.IO; | ||||||
|  | using API.Extensions; | ||||||
|  | using Xunit; | ||||||
|  | 
 | ||||||
|  | namespace API.Tests.Extensions | ||||||
| { | { | ||||||
|     public class FileInfoExtensionsTests |     public class FileInfoExtensionsTests | ||||||
|     { |     { | ||||||
|         // [Fact] |         private static readonly string TestDirectory = Path.Join(Directory.GetCurrentDirectory(), "../../../Extensions/Test Data/"); | ||||||
|         // public void DoesLastWriteMatchTest() | 
 | ||||||
|         // { |         [Fact] | ||||||
|         //     var fi = Substitute.For<FileInfo>(); |         public void HasFileBeenModifiedSince_ShouldBeFalse() | ||||||
|         //     fi.LastWriteTime = DateTime.Now; |         { | ||||||
|         //      |             var filepath = Path.Join(TestDirectory, "not modified.txt"); | ||||||
|         //     var deltaTime = DateTime.Today.Subtract(TimeSpan.FromDays(1)); |             var date = new FileInfo(filepath).LastWriteTime; | ||||||
|         //     Assert.False(fi.DoesLastWriteMatch(deltaTime)); |             Assert.False(new FileInfo(filepath).HasFileBeenModifiedSince(date)); | ||||||
|         // } |             File.ReadAllText(filepath); | ||||||
|         // |             Assert.False(new FileInfo(filepath).HasFileBeenModifiedSince(date)); | ||||||
|         // [Fact] |         } | ||||||
|         // public void IsLastWriteLessThanTest() | 
 | ||||||
|         // { |         [Fact] | ||||||
|         //      |         public void HasFileBeenModifiedSince_ShouldBeTrue() | ||||||
|         // } |         { | ||||||
|  |             var filepath = Path.Join(TestDirectory, "modified on run.txt"); | ||||||
|  |             var date = new FileInfo(filepath).LastWriteTime; | ||||||
|  |             Assert.False(new FileInfo(filepath).HasFileBeenModifiedSince(date)); | ||||||
|  |             File.AppendAllLines(filepath, new[] { DateTime.Now.ToString(CultureInfo.InvariantCulture) }); | ||||||
|  |             Assert.True(new FileInfo(filepath).HasFileBeenModifiedSince(date)); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								API.Tests/Extensions/Test Data/modified on run.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								API.Tests/Extensions/Test Data/modified on run.txt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | |||||||
|  | This file should be modified by the unit test08/20/2021 10:26:03 | ||||||
|  | 08/20/2021 10:26:29 | ||||||
							
								
								
									
										1
									
								
								API.Tests/Extensions/Test Data/not modified.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								API.Tests/Extensions/Test Data/not modified.txt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | Hello, this file should not be modified | ||||||
| @ -64,6 +64,7 @@ namespace API.Tests.Parser | |||||||
|         [InlineData("Sword Art Online Vol 10 - Alicization Running [Yen Press] [LuCaZ] {r2}.epub", "10")]
 |         [InlineData("Sword Art Online Vol 10 - Alicization Running [Yen Press] [LuCaZ] {r2}.epub", "10")]
 | ||||||
|         [InlineData("Noblesse - Episode 406 (52 Pages).7z", "0")] |         [InlineData("Noblesse - Episode 406 (52 Pages).7z", "0")] | ||||||
|         [InlineData("X-Men v1 #201 (September 2007).cbz", "1")] |         [InlineData("X-Men v1 #201 (September 2007).cbz", "1")] | ||||||
|  |         [InlineData("Hentai Ouji to Warawanai Neko. - Vol. 06 Ch. 034.5", "6")] | ||||||
|         public void ParseVolumeTest(string filename, string expected) |         public void ParseVolumeTest(string filename, string expected) | ||||||
|         { |         { | ||||||
|             Assert.Equal(expected, API.Parser.Parser.ParseVolume(filename)); |             Assert.Equal(expected, API.Parser.Parser.ParseVolume(filename)); | ||||||
| @ -154,6 +155,7 @@ namespace API.Tests.Parser | |||||||
|         [InlineData("Please Go Home, Akutsu-San! - Chapter 038.5 - Volume Announcement.cbz", "Please Go Home, Akutsu-San!")] |         [InlineData("Please Go Home, Akutsu-San! - Chapter 038.5 - Volume Announcement.cbz", "Please Go Home, Akutsu-San!")] | ||||||
|         [InlineData("Killing Bites - Vol 11 Chapter 050 Save Me, Nunupi!.cbz", "Killing Bites")] |         [InlineData("Killing Bites - Vol 11 Chapter 050 Save Me, Nunupi!.cbz", "Killing Bites")] | ||||||
|         [InlineData("Mad Chimera World - Volume 005 - Chapter 026.cbz", "Mad Chimera World")] |         [InlineData("Mad Chimera World - Volume 005 - Chapter 026.cbz", "Mad Chimera World")] | ||||||
|  |         [InlineData("Hentai Ouji to Warawanai Neko. - Vol. 06 Ch. 034.5", "Hentai Ouji to Warawanai Neko.")] | ||||||
|         public void ParseSeriesTest(string filename, string expected) |         public void ParseSeriesTest(string filename, string expected) | ||||||
|         { |         { | ||||||
|             Assert.Equal(expected, API.Parser.Parser.ParseSeries(filename)); |             Assert.Equal(expected, API.Parser.Parser.ParseSeries(filename)); | ||||||
| @ -222,6 +224,7 @@ namespace API.Tests.Parser | |||||||
|         [InlineData("Boku No Kokoro No Yabai Yatsu - Chapter 054 I Prayed At The Shrine (V0).cbz", "54")] |         [InlineData("Boku No Kokoro No Yabai Yatsu - Chapter 054 I Prayed At The Shrine (V0).cbz", "54")] | ||||||
|         [InlineData("Ijousha No Ai - Vol.01 Chapter 029 8 Years Ago", "29")] |         [InlineData("Ijousha No Ai - Vol.01 Chapter 029 8 Years Ago", "29")] | ||||||
|         [InlineData("Kedouin Makoto - Corpse Party Musume, Chapter 09.cbz", "9")] |         [InlineData("Kedouin Makoto - Corpse Party Musume, Chapter 09.cbz", "9")] | ||||||
|  |         [InlineData("Hentai Ouji to Warawanai Neko. - Vol. 06 Ch. 034.5", "34.5")] | ||||||
|         public void ParseChaptersTest(string filename, string expected) |         public void ParseChaptersTest(string filename, string expected) | ||||||
|         { |         { | ||||||
|             Assert.Equal(expected, API.Parser.Parser.ParseChapter(filename)); |             Assert.Equal(expected, API.Parser.Parser.ParseChapter(filename)); | ||||||
|  | |||||||
| @ -33,7 +33,6 @@ namespace API.Tests.Services | |||||||
|         private readonly IBookService _bookService = Substitute.For<IBookService>(); |         private readonly IBookService _bookService = Substitute.For<IBookService>(); | ||||||
|         private readonly IImageService _imageService = Substitute.For<IImageService>(); |         private readonly IImageService _imageService = Substitute.For<IImageService>(); | ||||||
|         private readonly ILogger<MetadataService> _metadataLogger = Substitute.For<ILogger<MetadataService>>(); |         private readonly ILogger<MetadataService> _metadataLogger = Substitute.For<ILogger<MetadataService>>(); | ||||||
|         private readonly IDirectoryService _directoryService = Substitute.For<IDirectoryService>(); |  | ||||||
|         private readonly ICacheService _cacheService = Substitute.For<ICacheService>(); |         private readonly ICacheService _cacheService = Substitute.For<ICacheService>(); | ||||||
| 
 | 
 | ||||||
|         private readonly DbConnection _connection; |         private readonly DbConnection _connection; | ||||||
|  | |||||||
| @ -1,5 +1,4 @@ | |||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| using System.IO; |  | ||||||
| using System.Linq; | using System.Linq; | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using API.DTOs; | using API.DTOs; | ||||||
|  | |||||||
| @ -70,7 +70,7 @@ namespace API.Controllers | |||||||
|                 { |                 { | ||||||
|                     return await GetFirstFileDownload(files); |                     return await GetFirstFileDownload(files); | ||||||
|                 } |                 } | ||||||
|                 var (fileBytes, zipPath) = await _archiveService.CreateZipForDownload(files.Select(c => c.FilePath), |                 var (fileBytes, _) = await _archiveService.CreateZipForDownload(files.Select(c => c.FilePath), | ||||||
|                     $"download_{User.GetUsername()}_v{volumeId}"); |                     $"download_{User.GetUsername()}_v{volumeId}"); | ||||||
|                 return File(fileBytes, DefaultContentType, $"{series.Name} - Volume {volume.Number}.zip"); |                 return File(fileBytes, DefaultContentType, $"{series.Name} - Volume {volume.Number}.zip"); | ||||||
|             } |             } | ||||||
| @ -116,7 +116,7 @@ namespace API.Controllers | |||||||
|                 { |                 { | ||||||
|                     return await GetFirstFileDownload(files); |                     return await GetFirstFileDownload(files); | ||||||
|                 } |                 } | ||||||
|                 var (fileBytes, zipPath) = await _archiveService.CreateZipForDownload(files.Select(c => c.FilePath), |                 var (fileBytes, _) = await _archiveService.CreateZipForDownload(files.Select(c => c.FilePath), | ||||||
|                     $"download_{User.GetUsername()}_c{chapterId}"); |                     $"download_{User.GetUsername()}_c{chapterId}"); | ||||||
|                 return File(fileBytes, DefaultContentType, $"{series.Name} - Chapter {chapter.Number}.zip"); |                 return File(fileBytes, DefaultContentType, $"{series.Name} - Chapter {chapter.Number}.zip"); | ||||||
|             } |             } | ||||||
| @ -137,7 +137,7 @@ namespace API.Controllers | |||||||
|                 { |                 { | ||||||
|                     return await GetFirstFileDownload(files); |                     return await GetFirstFileDownload(files); | ||||||
|                 } |                 } | ||||||
|                 var (fileBytes, zipPath) = await _archiveService.CreateZipForDownload(files.Select(c => c.FilePath), |                 var (fileBytes, _) = await _archiveService.CreateZipForDownload(files.Select(c => c.FilePath), | ||||||
|                     $"download_{User.GetUsername()}_s{seriesId}"); |                     $"download_{User.GetUsername()}_s{seriesId}"); | ||||||
|                 return File(fileBytes, DefaultContentType, $"{series.Name}.zip"); |                 return File(fileBytes, DefaultContentType, $"{series.Name}.zip"); | ||||||
|             } |             } | ||||||
| @ -194,11 +194,11 @@ namespace API.Controllers | |||||||
|                 var files = _directoryService.GetFilesWithExtension(chapterExtractPath, Parser.Parser.ImageFileExtensions); |                 var files = _directoryService.GetFilesWithExtension(chapterExtractPath, Parser.Parser.ImageFileExtensions); | ||||||
|                 // Filter out images that aren't in bookmarks |                 // Filter out images that aren't in bookmarks | ||||||
|                 Array.Sort(files, _numericComparer); |                 Array.Sort(files, _numericComparer); | ||||||
|                 totalFilePaths.AddRange(files.Where((t, i) => chapterPages.Contains(i))); |                 totalFilePaths.AddRange(files.Where((_, i) => chapterPages.Contains(i))); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|             var (fileBytes, zipPath) = await _archiveService.CreateZipForDownload(totalFilePaths, |             var (fileBytes, _) = await _archiveService.CreateZipForDownload(totalFilePaths, | ||||||
|                 tempFolder); |                 tempFolder); | ||||||
|             DirectoryService.ClearAndDeleteDirectory(fullExtractPath); |             DirectoryService.ClearAndDeleteDirectory(fullExtractPath); | ||||||
|             return File(fileBytes, DefaultContentType, $"{series.Name} - Bookmarks.zip"); |             return File(fileBytes, DefaultContentType, $"{series.Name} - Bookmarks.zip"); | ||||||
|  | |||||||
| @ -368,7 +368,7 @@ namespace API.Controllers | |||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Removes all bookmarks for all chapters linked to a Series |         /// Removes all bookmarks for all chapters linked to a Series | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         /// <param name="seriesId"></param> |         /// <param name="dto"></param> | ||||||
|         /// <returns></returns> |         /// <returns></returns> | ||||||
|         [HttpPost("remove-bookmarks")] |         [HttpPost("remove-bookmarks")] | ||||||
|         public async Task<ActionResult> RemoveBookmarks(RemoveBookmarkForSeriesDto dto) |         public async Task<ActionResult> RemoveBookmarks(RemoveBookmarkForSeriesDto dto) | ||||||
|  | |||||||
| @ -12,7 +12,6 @@ using API.Interfaces; | |||||||
| using Kavita.Common; | using Kavita.Common; | ||||||
| using Microsoft.AspNetCore.Authorization; | using Microsoft.AspNetCore.Authorization; | ||||||
| using Microsoft.AspNetCore.Mvc; | using Microsoft.AspNetCore.Mvc; | ||||||
| using Microsoft.EntityFrameworkCore; |  | ||||||
| using Microsoft.Extensions.Logging; | using Microsoft.Extensions.Logging; | ||||||
| 
 | 
 | ||||||
| namespace API.Controllers | namespace API.Controllers | ||||||
|  | |||||||
| @ -5,7 +5,6 @@ using System.Threading.Tasks; | |||||||
| using API.DTOs.Stats; | using API.DTOs.Stats; | ||||||
| using API.DTOs.Update; | using API.DTOs.Update; | ||||||
| using API.Extensions; | using API.Extensions; | ||||||
| using API.Interfaces; |  | ||||||
| using API.Interfaces.Services; | using API.Interfaces.Services; | ||||||
| using API.Services.Tasks; | using API.Services.Tasks; | ||||||
| using Kavita.Common; | using Kavita.Common; | ||||||
| @ -26,11 +25,10 @@ namespace API.Controllers | |||||||
|         private readonly IBackupService _backupService; |         private readonly IBackupService _backupService; | ||||||
|         private readonly IArchiveService _archiveService; |         private readonly IArchiveService _archiveService; | ||||||
|         private readonly ICacheService _cacheService; |         private readonly ICacheService _cacheService; | ||||||
|         private readonly ITaskScheduler _taskScheduler; |  | ||||||
|         private readonly IVersionUpdaterService _versionUpdaterService; |         private readonly IVersionUpdaterService _versionUpdaterService; | ||||||
| 
 | 
 | ||||||
|         public ServerController(IHostApplicationLifetime applicationLifetime, ILogger<ServerController> logger, IConfiguration config, |         public ServerController(IHostApplicationLifetime applicationLifetime, ILogger<ServerController> logger, IConfiguration config, | ||||||
|             IBackupService backupService, IArchiveService archiveService, ICacheService cacheService, ITaskScheduler taskScheduler, |             IBackupService backupService, IArchiveService archiveService, ICacheService cacheService, | ||||||
|             IVersionUpdaterService versionUpdaterService) |             IVersionUpdaterService versionUpdaterService) | ||||||
|         { |         { | ||||||
|             _applicationLifetime = applicationLifetime; |             _applicationLifetime = applicationLifetime; | ||||||
| @ -39,7 +37,6 @@ namespace API.Controllers | |||||||
|             _backupService = backupService; |             _backupService = backupService; | ||||||
|             _archiveService = archiveService; |             _archiveService = archiveService; | ||||||
|             _cacheService = cacheService; |             _cacheService = cacheService; | ||||||
|             _taskScheduler = taskScheduler; |  | ||||||
|             _versionUpdaterService = versionUpdaterService; |             _versionUpdaterService = versionUpdaterService; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,6 +1,4 @@ | |||||||
| using System.Collections.Generic; | namespace API.DTOs | ||||||
| 
 |  | ||||||
| namespace API.DTOs |  | ||||||
| { | { | ||||||
|     public class SeriesByIdsDto |     public class SeriesByIdsDto | ||||||
|     { |     { | ||||||
|  | |||||||
| @ -1,6 +1,5 @@ | |||||||
| using API.Entities; | using API.Entities; | ||||||
| using API.Interfaces.Repositories; | using API.Interfaces.Repositories; | ||||||
| using AutoMapper; |  | ||||||
| using Microsoft.EntityFrameworkCore; | using Microsoft.EntityFrameworkCore; | ||||||
| 
 | 
 | ||||||
| namespace API.Data | namespace API.Data | ||||||
|  | |||||||
| @ -60,11 +60,11 @@ namespace API.Data | |||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             await context.SaveChangesAsync(); |             await context.SaveChangesAsync(); | ||||||
|              | 
 | ||||||
|             // Port and LoggingLevel are managed in appSettings.json. Update the DB values to match |             // Port and LoggingLevel are managed in appSettings.json. Update the DB values to match | ||||||
|             context.ServerSetting.FirstOrDefault(s => s.Key == ServerSettingKey.Port).Value = |             context.ServerSetting.First(s => s.Key == ServerSettingKey.Port).Value = | ||||||
|                 Configuration.Port + string.Empty; |                 Configuration.Port + string.Empty; | ||||||
|             context.ServerSetting.FirstOrDefault(s => s.Key == ServerSettingKey.LoggingLevel).Value = |             context.ServerSetting.First(s => s.Key == ServerSettingKey.LoggingLevel).Value = | ||||||
|                 Configuration.LogLevel + string.Empty; |                 Configuration.LogLevel + string.Empty; | ||||||
| 
 | 
 | ||||||
|             await context.SaveChangesAsync(); |             await context.SaveChangesAsync(); | ||||||
| @ -74,11 +74,11 @@ namespace API.Data | |||||||
|         public static async Task SeedSeriesMetadata(DataContext context) |         public static async Task SeedSeriesMetadata(DataContext context) | ||||||
|         { |         { | ||||||
|             await context.Database.EnsureCreatedAsync(); |             await context.Database.EnsureCreatedAsync(); | ||||||
|              | 
 | ||||||
|             context.Database.EnsureCreated(); |             context.Database.EnsureCreated(); | ||||||
|             var series = await context.Series |             var series = await context.Series | ||||||
|                 .Include(s => s.Metadata).ToListAsync(); |                 .Include(s => s.Metadata).ToListAsync(); | ||||||
|                  | 
 | ||||||
|             foreach (var s in series) |             foreach (var s in series) | ||||||
|             { |             { | ||||||
|                 s.Metadata ??= new SeriesMetadata(); |                 s.Metadata ??= new SeriesMetadata(); | ||||||
|  | |||||||
| @ -1,12 +1,10 @@ | |||||||
| using System; | using System.Collections.Generic; | ||||||
| using System.Collections.Generic; |  | ||||||
| using System.Linq; | using System.Linq; | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using API.Comparators; | using API.Comparators; | ||||||
| using API.DTOs; | using API.DTOs; | ||||||
| using API.DTOs.Filtering; | using API.DTOs.Filtering; | ||||||
| using API.Entities; | using API.Entities; | ||||||
| using API.Entities.Enums; |  | ||||||
| using API.Extensions; | using API.Extensions; | ||||||
| using API.Helpers; | using API.Helpers; | ||||||
| using API.Interfaces; | using API.Interfaces; | ||||||
|  | |||||||
| @ -1,9 +1,7 @@ | |||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| using System.IO; |  | ||||||
| using System.Linq; | using System.Linq; | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using API.DTOs; | using API.DTOs; | ||||||
| using API.DTOs.Reader; |  | ||||||
| using API.Entities; | using API.Entities; | ||||||
| using API.Interfaces; | using API.Interfaces; | ||||||
| using AutoMapper; | using AutoMapper; | ||||||
|  | |||||||
| @ -2,7 +2,6 @@ | |||||||
| using System; | using System; | ||||||
| using System.ComponentModel.DataAnnotations; | using System.ComponentModel.DataAnnotations; | ||||||
| using API.Entities.Interfaces; | using API.Entities.Interfaces; | ||||||
| using Microsoft.EntityFrameworkCore; |  | ||||||
| 
 | 
 | ||||||
| namespace API.Entities | namespace API.Entities | ||||||
| { | { | ||||||
|  | |||||||
| @ -1,6 +1,4 @@ | |||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| using System.ComponentModel.DataAnnotations; |  | ||||||
| using API.Entities.Interfaces; |  | ||||||
| using Microsoft.EntityFrameworkCore; | using Microsoft.EntityFrameworkCore; | ||||||
| 
 | 
 | ||||||
| namespace API.Entities | namespace API.Entities | ||||||
|  | |||||||
| @ -1,5 +1,4 @@ | |||||||
| using System; | using System.IO; | ||||||
| using System.IO; |  | ||||||
| using System.Linq; | using System.Linq; | ||||||
| using API.Comparators; | using API.Comparators; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -5,14 +5,15 @@ namespace API.Extensions | |||||||
| { | { | ||||||
|     public static class FileInfoExtensions |     public static class FileInfoExtensions | ||||||
|     { |     { | ||||||
|         public static bool DoesLastWriteMatch(this FileInfo fileInfo, DateTime comparison) |         /// <summary> | ||||||
|  |         /// Checks if the last write time of the file is after the passed date | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="fileInfo"></param> | ||||||
|  |         /// <param name="comparison"></param> | ||||||
|  |         /// <returns></returns> | ||||||
|  |         public static bool HasFileBeenModifiedSince(this FileInfo fileInfo, DateTime comparison) | ||||||
|         { |         { | ||||||
|             return comparison.Equals(fileInfo.LastWriteTime); |             return fileInfo?.LastWriteTime > comparison; | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static bool IsLastWriteLessThan(this FileInfo fileInfo, DateTime comparison) |  | ||||||
|         { |  | ||||||
|             return fileInfo.LastWriteTime < comparison; |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,8 +1,6 @@ | |||||||
| using System; | using System.Collections.Generic; | ||||||
| using System.Collections.Generic; |  | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using API.DTOs.Update; | using API.DTOs.Update; | ||||||
| using API.Services.Tasks; |  | ||||||
| 
 | 
 | ||||||
| namespace API.Interfaces.Services | namespace API.Interfaces.Services | ||||||
| { | { | ||||||
|  | |||||||
| @ -30,6 +30,7 @@ namespace API.Services | |||||||
|         private readonly IDirectoryService _directoryService; |         private readonly IDirectoryService _directoryService; | ||||||
|         private static readonly RecyclableMemoryStreamManager StreamManager = new(); |         private static readonly RecyclableMemoryStreamManager StreamManager = new(); | ||||||
|         private readonly NaturalSortComparer _comparer; |         private readonly NaturalSortComparer _comparer; | ||||||
|  |         private const string ComicInfoFilename = "comicinfo"; | ||||||
| 
 | 
 | ||||||
|         public ArchiveService(ILogger<ArchiveService> logger, IDirectoryService directoryService) |         public ArchiveService(ILogger<ArchiveService> logger, IDirectoryService directoryService) | ||||||
|         { |         { | ||||||
| @ -297,7 +298,7 @@ namespace API.Services | |||||||
|             foreach (var entry in entries) |             foreach (var entry in entries) | ||||||
|             { |             { | ||||||
|                 var filename = Path.GetFileNameWithoutExtension(entry.Key).ToLower(); |                 var filename = Path.GetFileNameWithoutExtension(entry.Key).ToLower(); | ||||||
|                 if (filename.EndsWith("comicinfo") |                 if (filename.EndsWith(ComicInfoFilename) | ||||||
|                     && !filename.StartsWith(Parser.Parser.MacOsMetadataFileStartsWith) |                     && !filename.StartsWith(Parser.Parser.MacOsMetadataFileStartsWith) | ||||||
|                     && !Parser.Parser.HasBlacklistedFolderInPath(entry.Key) |                     && !Parser.Parser.HasBlacklistedFolderInPath(entry.Key) | ||||||
|                     && Parser.Parser.IsXml(entry.Key)) |                     && Parser.Parser.IsXml(entry.Key)) | ||||||
| @ -334,7 +335,7 @@ namespace API.Services | |||||||
|                         _logger.LogDebug("Using default compression handling"); |                         _logger.LogDebug("Using default compression handling"); | ||||||
|                         using var archive = ZipFile.OpenRead(archivePath); |                         using var archive = ZipFile.OpenRead(archivePath); | ||||||
|                         var entry = archive.Entries.SingleOrDefault(x => !Parser.Parser.HasBlacklistedFolderInPath(x.FullName) |                         var entry = archive.Entries.SingleOrDefault(x => !Parser.Parser.HasBlacklistedFolderInPath(x.FullName) | ||||||
|                                                                          && Path.GetFileNameWithoutExtension(x.Name).ToLower() == "comicinfo" |                                                                          && Path.GetFileNameWithoutExtension(x.Name)?.ToLower() == ComicInfoFilename | ||||||
|                                                                          && !Path.GetFileNameWithoutExtension(x.Name).StartsWith(Parser.Parser.MacOsMetadataFileStartsWith) |                                                                          && !Path.GetFileNameWithoutExtension(x.Name).StartsWith(Parser.Parser.MacOsMetadataFileStartsWith) | ||||||
|                                                                          && Parser.Parser.IsXml(x.FullName)); |                                                                          && Parser.Parser.IsXml(x.FullName)); | ||||||
|                         if (entry != null) |                         if (entry != null) | ||||||
|  | |||||||
| @ -9,7 +9,6 @@ using API.Entities.Enums; | |||||||
| using API.Extensions; | using API.Extensions; | ||||||
| using API.Interfaces; | using API.Interfaces; | ||||||
| using API.Interfaces.Services; | using API.Interfaces.Services; | ||||||
| using Kavita.Common; |  | ||||||
| using Microsoft.Extensions.Logging; | using Microsoft.Extensions.Logging; | ||||||
| 
 | 
 | ||||||
| namespace API.Services | namespace API.Services | ||||||
|  | |||||||
| @ -11,7 +11,9 @@ namespace API.Services.Clients | |||||||
|     { |     { | ||||||
|         private readonly HttpClient _client; |         private readonly HttpClient _client; | ||||||
|         private readonly ILogger<StatsApiClient> _logger; |         private readonly ILogger<StatsApiClient> _logger; | ||||||
|  | #pragma warning disable S1075 | ||||||
|         private const string ApiUrl = "http://stats.kavitareader.com"; |         private const string ApiUrl = "http://stats.kavitareader.com"; | ||||||
|  | #pragma warning restore S1075 | ||||||
| 
 | 
 | ||||||
|         public StatsApiClient(HttpClient client, ILogger<StatsApiClient> logger) |         public StatsApiClient(HttpClient client, ILogger<StatsApiClient> logger) | ||||||
|         { |         { | ||||||
|  | |||||||
| @ -320,7 +320,7 @@ namespace API.Services | |||||||
|             var fileCount = 0; |             var fileCount = 0; | ||||||
| 
 | 
 | ||||||
|             // Determine whether to parallelize file processing on each folder based on processor count. |             // Determine whether to parallelize file processing on each folder based on processor count. | ||||||
|             var procCount = Environment.ProcessorCount; |             //var procCount = Environment.ProcessorCount; | ||||||
| 
 | 
 | ||||||
|             // Data structure to hold names of subfolders to be examined for files. |             // Data structure to hold names of subfolders to be examined for files. | ||||||
|             var dirs = new Stack<string>(); |             var dirs = new Stack<string>(); | ||||||
|  | |||||||
| @ -67,7 +67,10 @@ namespace API.Services | |||||||
|         public void UpdateMetadata(Chapter chapter, bool forceUpdate) |         public void UpdateMetadata(Chapter chapter, bool forceUpdate) | ||||||
|         { |         { | ||||||
|             var firstFile = chapter.Files.OrderBy(x => x.Chapter).FirstOrDefault(); |             var firstFile = chapter.Files.OrderBy(x => x.Chapter).FirstOrDefault(); | ||||||
|             if (!chapter.CoverImageLocked && ShouldFindCoverImage(chapter.CoverImage, forceUpdate) && firstFile != null && !new FileInfo(firstFile.FilePath).IsLastWriteLessThan(firstFile.LastModified)) |             if (!chapter.CoverImageLocked | ||||||
|  |                 && ShouldFindCoverImage(chapter.CoverImage, forceUpdate) | ||||||
|  |                 && firstFile != null | ||||||
|  |                 && (forceUpdate || new FileInfo(firstFile.FilePath).HasFileBeenModifiedSince(firstFile.LastModified))) | ||||||
|             { |             { | ||||||
|                 chapter.Files ??= new List<MangaFile>(); |                 chapter.Files ??= new List<MangaFile>(); | ||||||
|                 chapter.CoverImage = GetCoverImage(firstFile); |                 chapter.CoverImage = GetCoverImage(firstFile); | ||||||
| @ -88,19 +91,7 @@ namespace API.Services | |||||||
| 
 | 
 | ||||||
|             if (firstChapter == null) return; |             if (firstChapter == null) return; | ||||||
| 
 | 
 | ||||||
|             // Skip calculating Cover Image (I/O) if the chapter already has it set |  | ||||||
|             if (!firstChapter.CoverImageLocked && ShouldFindCoverImage(firstChapter.CoverImage, forceUpdate)) |  | ||||||
|             { |  | ||||||
|                 // NOTE: Why do I do this? By the time this method gets executed, the chapter has already been calculated for |  | ||||||
|                 // Plus how can we have a volume without at least 1 chapter? |  | ||||||
|                 var firstFile = firstChapter.Files.OrderBy(x => x.Chapter).FirstOrDefault(); |  | ||||||
|                 if (firstFile != null && !new FileInfo(firstFile.FilePath).IsLastWriteLessThan(firstFile.LastModified)) |  | ||||||
|                 { |  | ||||||
|                     firstChapter.CoverImage = GetCoverImage(firstFile); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             volume.CoverImage = firstChapter.CoverImage; |             volume.CoverImage = firstChapter.CoverImage; | ||||||
| 
 |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
|  | |||||||
| @ -119,7 +119,6 @@ namespace API.Services | |||||||
|             BackgroundJob.Enqueue(() => _cleanupService.Cleanup()); |             BackgroundJob.Enqueue(() => _cleanupService.Cleanup()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|         public void CleanupChapters(int[] chapterIds) |         public void CleanupChapters(int[] chapterIds) | ||||||
|         { |         { | ||||||
|             BackgroundJob.Enqueue(() => _cacheService.CleanupChapters(chapterIds)); |             BackgroundJob.Enqueue(() => _cacheService.CleanupChapters(chapterIds)); | ||||||
|  | |||||||
| @ -74,7 +74,7 @@ namespace API.Services.Tasks | |||||||
|                     totalFiles, parsedSeries.Keys.Count, sw.ElapsedMilliseconds + scanElapsedTime, series.Name); |                     totalFiles, parsedSeries.Keys.Count, sw.ElapsedMilliseconds + scanElapsedTime, series.Name); | ||||||
| 
 | 
 | ||||||
|                 CleanupDbEntities(); |                 CleanupDbEntities(); | ||||||
|                 BackgroundJob.Enqueue(() => _metadataService.RefreshMetadata(libraryId, forceUpdate)); |                 BackgroundJob.Enqueue(() => _metadataService.RefreshMetadataForSeries(libraryId, seriesId)); | ||||||
|                 BackgroundJob.Enqueue(() => _cacheService.CleanupChapters(chapterIds)); |                 BackgroundJob.Enqueue(() => _cacheService.CleanupChapters(chapterIds)); | ||||||
|             } |             } | ||||||
|             else |             else | ||||||
| @ -132,11 +132,14 @@ namespace API.Services.Tasks | |||||||
|           { |           { | ||||||
|              ScanLibrary(lib.Id, false); |              ScanLibrary(lib.Id, false); | ||||||
|           } |           } | ||||||
|  | 
 | ||||||
|        } |        } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|        /// <summary> |        /// <summary> | ||||||
|        /// Scans a library for file changes. If force update passed, all entities will be rechecked for new cover images and comicInfo.xml changes. |        /// Scans a library for file changes. | ||||||
|  |        /// Will kick off a scheduled background task to refresh metadata, | ||||||
|  |        /// ie) all entities will be rechecked for new cover images and comicInfo.xml changes | ||||||
|        /// </summary> |        /// </summary> | ||||||
|        /// <param name="libraryId"></param> |        /// <param name="libraryId"></param> | ||||||
|        /// <param name="forceUpdate"></param> |        /// <param name="forceUpdate"></param> | ||||||
|  | |||||||
| @ -42,7 +42,7 @@ namespace API.Services.Tasks | |||||||
|     { |     { | ||||||
|         public override HttpMessageHandler CreateMessageHandler() { |         public override HttpMessageHandler CreateMessageHandler() { | ||||||
|             return new HttpClientHandler { |             return new HttpClientHandler { | ||||||
|                 ServerCertificateCustomValidationCallback = (a, b, c, d) => true |                 ServerCertificateCustomValidationCallback = (_, _, _, _) => true | ||||||
|             }; |             }; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -87,7 +87,7 @@ namespace API.Services.Tasks | |||||||
|             return updates.Select(CreateDto); |             return updates.Select(CreateDto); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         private UpdateNotificationDto? CreateDto(GithubReleaseMetadata update) |         private UpdateNotificationDto CreateDto(GithubReleaseMetadata update) | ||||||
|         { |         { | ||||||
|             if (update == null || string.IsNullOrEmpty(update.Tag_Name)) return null; |             if (update == null || string.IsNullOrEmpty(update.Tag_Name)) return null; | ||||||
|             var version = update.Tag_Name.Replace("v", string.Empty); |             var version = update.Tag_Name.Replace("v", string.Empty); | ||||||
|  | |||||||
| @ -49,7 +49,7 @@ namespace API | |||||||
|             services.AddSwaggerGen(c => |             services.AddSwaggerGen(c => | ||||||
|             { |             { | ||||||
|                 c.SwaggerDoc("v1", new OpenApiInfo { Title = "Kavita API", Version = "v1" }); |                 c.SwaggerDoc("v1", new OpenApiInfo { Title = "Kavita API", Version = "v1" }); | ||||||
|                 var filePath = Path.Combine(System.AppContext.BaseDirectory, "API.xml"); |                 var filePath = Path.Combine(AppContext.BaseDirectory, "API.xml"); | ||||||
|                 c.IncludeXmlComments(filePath); |                 c.IncludeXmlComments(filePath); | ||||||
|             }); |             }); | ||||||
|             services.AddResponseCompression(options => |             services.AddResponseCompression(options => | ||||||
|  | |||||||
| @ -8,7 +8,7 @@ namespace Kavita.Common | |||||||
| { | { | ||||||
|    public static class Configuration |    public static class Configuration | ||||||
|    { |    { | ||||||
|       private static string AppSettingsFilename = GetAppSettingFilename(); |       private static readonly string AppSettingsFilename = GetAppSettingFilename(); | ||||||
|       public static string Branch |       public static string Branch | ||||||
|       { |       { | ||||||
|          get => GetBranch(GetAppSettingFilename()); |          get => GetBranch(GetAppSettingFilename()); | ||||||
| @ -53,6 +53,7 @@ namespace Kavita.Common | |||||||
|             var json = File.ReadAllText(filePath); |             var json = File.ReadAllText(filePath); | ||||||
|             var jsonObj = JsonSerializer.Deserialize<dynamic>(json); |             var jsonObj = JsonSerializer.Deserialize<dynamic>(json); | ||||||
|             const string key = "TokenKey"; |             const string key = "TokenKey"; | ||||||
|  |             if (jsonObj == null) return string.Empty; | ||||||
| 
 | 
 | ||||||
|             if (jsonObj.TryGetProperty(key, out JsonElement tokenElement)) |             if (jsonObj.TryGetProperty(key, out JsonElement tokenElement)) | ||||||
|             { |             { | ||||||
| @ -69,7 +70,7 @@ namespace Kavita.Common | |||||||
|          return string.Empty; |          return string.Empty; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       private static bool SetJwtToken(string filePath, string token) |       private static void SetJwtToken(string filePath, string token) | ||||||
|       { |       { | ||||||
|          try |          try | ||||||
|          { |          { | ||||||
| @ -77,53 +78,37 @@ namespace Kavita.Common | |||||||
|             var json = File.ReadAllText(filePath) |             var json = File.ReadAllText(filePath) | ||||||
|                .Replace("\"TokenKey\": \"" + currentToken, "\"TokenKey\": \"" + token); |                .Replace("\"TokenKey\": \"" + currentToken, "\"TokenKey\": \"" + token); | ||||||
|             File.WriteAllText(filePath, json); |             File.WriteAllText(filePath, json); | ||||||
|             return true; |  | ||||||
|          } |          } | ||||||
|          catch (Exception) |          catch (Exception) | ||||||
|          { |          { | ||||||
|             return false; |              /* Swallow exception */ | ||||||
|          } |          } | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       public static bool CheckIfJwtTokenSet() |       public static bool CheckIfJwtTokenSet() | ||||||
|       { |       { | ||||||
|          //string filePath |           try | ||||||
|          try |           { | ||||||
|          { |               return GetJwtToken(GetAppSettingFilename()) != "super secret unguessable key"; | ||||||
|             return GetJwtToken(GetAppSettingFilename()) != "super secret unguessable key"; |           } | ||||||
|          } |           catch (Exception ex) | ||||||
|          catch (Exception ex) |           { | ||||||
|          { |               Console.WriteLine("Error writing app settings: " + ex.Message); | ||||||
|             Console.WriteLine("Error writing app settings: " + ex.Message); |           } | ||||||
|          } |  | ||||||
| 
 | 
 | ||||||
|          return false; |           return false; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       public static bool UpdateJwtToken(string token) |  | ||||||
|       { |  | ||||||
|          try |  | ||||||
|          { |  | ||||||
|             var filePath = GetAppSettingFilename(); |  | ||||||
|             var json = File.ReadAllText(filePath).Replace("super secret unguessable key", token); |  | ||||||
|             File.WriteAllText(GetAppSettingFilename(), json); |  | ||||||
|             return true; |  | ||||||
|          } |  | ||||||
|          catch (Exception) |  | ||||||
|          { |  | ||||||
|             return false; |  | ||||||
|          } |  | ||||||
|       } |  | ||||||
| 
 | 
 | ||||||
|       #endregion |       #endregion | ||||||
| 
 | 
 | ||||||
|       #region Port |       #region Port | ||||||
| 
 | 
 | ||||||
|       public static bool SetPort(string filePath, int port) |       private static void SetPort(string filePath, int port) | ||||||
|       { |       { | ||||||
|          if (new OsInfo(Array.Empty<IOsVersionAdapter>()).IsDocker) |          if (new OsInfo(Array.Empty<IOsVersionAdapter>()).IsDocker) | ||||||
|          { |          { | ||||||
|             return true; |             return; | ||||||
|          } |          } | ||||||
| 
 | 
 | ||||||
|          try |          try | ||||||
| @ -131,15 +116,14 @@ namespace Kavita.Common | |||||||
|             var currentPort = GetPort(filePath); |             var currentPort = GetPort(filePath); | ||||||
|             var json = File.ReadAllText(filePath).Replace("\"Port\": " + currentPort, "\"Port\": " + port); |             var json = File.ReadAllText(filePath).Replace("\"Port\": " + currentPort, "\"Port\": " + port); | ||||||
|             File.WriteAllText(filePath, json); |             File.WriteAllText(filePath, json); | ||||||
|             return true; |  | ||||||
|          } |          } | ||||||
|          catch (Exception) |          catch (Exception) | ||||||
|          { |          { | ||||||
|             return false; |             /* Swallow Exception */ | ||||||
|          } |          } | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       public static int GetPort(string filePath) |       private static int GetPort(string filePath) | ||||||
|       { |       { | ||||||
|           const int defaultPort = 5000; |           const int defaultPort = 5000; | ||||||
|          if (new OsInfo(Array.Empty<IOsVersionAdapter>()).IsDocker) |          if (new OsInfo(Array.Empty<IOsVersionAdapter>()).IsDocker) | ||||||
| @ -152,6 +136,7 @@ namespace Kavita.Common | |||||||
|             var json = File.ReadAllText(filePath); |             var json = File.ReadAllText(filePath); | ||||||
|             var jsonObj = JsonSerializer.Deserialize<dynamic>(json); |             var jsonObj = JsonSerializer.Deserialize<dynamic>(json); | ||||||
|             const string key = "Port"; |             const string key = "Port"; | ||||||
|  |             if (jsonObj == null) return defaultPort; | ||||||
| 
 | 
 | ||||||
|             if (jsonObj.TryGetProperty(key, out JsonElement tokenElement)) |             if (jsonObj.TryGetProperty(key, out JsonElement tokenElement)) | ||||||
|             { |             { | ||||||
| @ -170,7 +155,7 @@ namespace Kavita.Common | |||||||
| 
 | 
 | ||||||
|       #region LogLevel |       #region LogLevel | ||||||
| 
 | 
 | ||||||
|       public static bool SetLogLevel(string filePath, string logLevel) |       private static void SetLogLevel(string filePath, string logLevel) | ||||||
|       { |       { | ||||||
|          try |          try | ||||||
|          { |          { | ||||||
| @ -178,20 +163,21 @@ namespace Kavita.Common | |||||||
|             var json = File.ReadAllText(filePath) |             var json = File.ReadAllText(filePath) | ||||||
|                .Replace($"\"Default\": \"{currentLevel}\"", $"\"Default\": \"{logLevel}\""); |                .Replace($"\"Default\": \"{currentLevel}\"", $"\"Default\": \"{logLevel}\""); | ||||||
|             File.WriteAllText(filePath, json); |             File.WriteAllText(filePath, json); | ||||||
|             return true; |  | ||||||
|          } |          } | ||||||
|          catch (Exception) |          catch (Exception) | ||||||
|          { |          { | ||||||
|             return false; |             /* Swallow Exception */ | ||||||
|          } |          } | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       public static string GetLogLevel(string filePath) |       private static string GetLogLevel(string filePath) | ||||||
|       { |       { | ||||||
|          try |          try | ||||||
|          { |          { | ||||||
|             var json = File.ReadAllText(filePath); |             var json = File.ReadAllText(filePath); | ||||||
|             var jsonObj = JsonSerializer.Deserialize<dynamic>(json); |             var jsonObj = JsonSerializer.Deserialize<dynamic>(json); | ||||||
|  |             if (jsonObj == null) return string.Empty; | ||||||
|  | 
 | ||||||
|             if (jsonObj.TryGetProperty("Logging", out JsonElement tokenElement)) |             if (jsonObj.TryGetProperty("Logging", out JsonElement tokenElement)) | ||||||
|             { |             { | ||||||
|                foreach (var property in tokenElement.EnumerateObject()) |                foreach (var property in tokenElement.EnumerateObject()) | ||||||
| @ -217,7 +203,7 @@ namespace Kavita.Common | |||||||
| 
 | 
 | ||||||
|       #endregion |       #endregion | ||||||
| 
 | 
 | ||||||
|       public static string GetBranch(string filePath) |       private static string GetBranch(string filePath) | ||||||
|       { |       { | ||||||
|          const string defaultBranch = "main"; |          const string defaultBranch = "main"; | ||||||
| 
 | 
 | ||||||
| @ -226,6 +212,7 @@ namespace Kavita.Common | |||||||
|             var json = File.ReadAllText(filePath); |             var json = File.ReadAllText(filePath); | ||||||
|             var jsonObj = JsonSerializer.Deserialize<dynamic>(json); |             var jsonObj = JsonSerializer.Deserialize<dynamic>(json); | ||||||
|             const string key = "Branch"; |             const string key = "Branch"; | ||||||
|  |             if (jsonObj == null) return string.Empty; | ||||||
| 
 | 
 | ||||||
|             if (jsonObj.TryGetProperty(key, out JsonElement tokenElement)) |             if (jsonObj.TryGetProperty(key, out JsonElement tokenElement)) | ||||||
|             { |             { | ||||||
| @ -240,7 +227,7 @@ namespace Kavita.Common | |||||||
|          return defaultBranch; |          return defaultBranch; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       public static bool SetBranch(string filePath, string updatedBranch) |       private static void SetBranch(string filePath, string updatedBranch) | ||||||
|       { |       { | ||||||
|          try |          try | ||||||
|          { |          { | ||||||
| @ -248,11 +235,10 @@ namespace Kavita.Common | |||||||
|             var json = File.ReadAllText(filePath) |             var json = File.ReadAllText(filePath) | ||||||
|                .Replace("\"Branch\": " + currentBranch, "\"Branch\": " + updatedBranch); |                .Replace("\"Branch\": " + currentBranch, "\"Branch\": " + updatedBranch); | ||||||
|             File.WriteAllText(filePath, json); |             File.WriteAllText(filePath, json); | ||||||
|             return true; |  | ||||||
|          } |          } | ||||||
|          catch (Exception) |          catch (Exception) | ||||||
|          { |          { | ||||||
|             return false; |             /* Swallow Exception */ | ||||||
|          } |          } | ||||||
|       } |       } | ||||||
|    } |    } | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| using System; | using System; | ||||||
|  | using System.Runtime.Serialization; | ||||||
| 
 | 
 | ||||||
| namespace Kavita.Common | namespace Kavita.Common | ||||||
| { | { | ||||||
| @ -9,13 +10,16 @@ namespace Kavita.Common | |||||||
|     public class KavitaException : Exception |     public class KavitaException : Exception | ||||||
|     { |     { | ||||||
|         public KavitaException() |         public KavitaException() | ||||||
|         { |         { } | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         public KavitaException(string message) : base(message) |         public KavitaException(string message) : base(message) | ||||||
|         { |         { } | ||||||
| 
 | 
 | ||||||
|         } |         public KavitaException(string message, Exception inner) | ||||||
|  |             : base(message, inner) { } | ||||||
|  | 
 | ||||||
|  |         protected KavitaException(SerializationInfo info, StreamingContext context) | ||||||
|  |             : base(info, context) | ||||||
|  |         { } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -7,7 +7,7 @@ import { MessageHubService } from './_services/message-hub.service'; | |||||||
| import { NavService } from './_services/nav.service'; | import { NavService } from './_services/nav.service'; | ||||||
| import { PresenceHubService } from './_services/presence-hub.service'; | import { PresenceHubService } from './_services/presence-hub.service'; | ||||||
| import { StatsService } from './_services/stats.service'; | import { StatsService } from './_services/stats.service'; | ||||||
| import 'rxjs/add/operator/filter'; | import { filter } from 'rxjs/operators'; | ||||||
| import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; | import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; | ||||||
| 
 | 
 | ||||||
| @Component({ | @Component({ | ||||||
| @ -23,7 +23,7 @@ export class AppComponent implements OnInit { | |||||||
|      |      | ||||||
|     // Close any open modals when a route change occurs
 |     // Close any open modals when a route change occurs
 | ||||||
|     router.events |     router.events | ||||||
|       .filter(event => event instanceof NavigationStart) |       .pipe(filter(event => event instanceof NavigationStart)) | ||||||
|       .subscribe((event) => { |       .subscribe((event) => { | ||||||
|         if (this.ngbModal.hasOpenModals()) { |         if (this.ngbModal.hasOpenModals()) { | ||||||
|           this.ngbModal.dismissAll(); |           this.ngbModal.dismissAll(); | ||||||
|  | |||||||
| @ -461,7 +461,7 @@ export class SeriesDetailComponent implements OnInit { | |||||||
|         }), |         }), | ||||||
|         finalize(() => { |         finalize(() => { | ||||||
|           this.downloadInProgress = false; |           this.downloadInProgress = false; | ||||||
|         })); |         })).subscribe(() => {/* No Operation */});; | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user