diff --git a/API.Tests/Helpers/SmartFilterHelperTests.cs b/API.Tests/Helpers/SmartFilterHelperTests.cs index 5508ab1a7..974cb0ba6 100644 --- a/API.Tests/Helpers/SmartFilterHelperTests.cs +++ b/API.Tests/Helpers/SmartFilterHelperTests.cs @@ -44,6 +44,17 @@ public class SmartFilterHelperTests AssertStatementSame(list[0], FilterField.Genres, FilterComparison.Equal, "95"); } + [Fact] + public void Test_Decode2() + { + const string encoded = """ + name=Test%202&stmts=comparison%253D10%25C2%25A6field%253D1%25C2%25A6value%253DA%EF%BF%BDcomparison%253D0%25C2%25A6field%253D19%25C2%25A6value%253D11&sortOptions=sortField%3D1%C2%A6isAscending%3DTrue&limitTo=0&combination=1 + """; + + var filter = SmartFilterHelper.Decode(encoded); + Assert.True(filter.SortOptions.IsAscending); + } + [Fact] public void Test_EncodeDecode() { diff --git a/API.Tests/Parser/MangaParserTests.cs b/API.Tests/Parser/MangaParserTests.cs index f89a411e6..5ba6a35b7 100644 --- a/API.Tests/Parser/MangaParserTests.cs +++ b/API.Tests/Parser/MangaParserTests.cs @@ -83,6 +83,7 @@ public class MangaParserTests [InlineData("시즌34삽화2", "34")] [InlineData("Accel World Chapter 001 Volume 002", "2")] [InlineData("Accel World Volume 2", "2")] + [InlineData("Nagasarete Airantou - Vol. 30 Ch. 187.5 - Vol.31 Omake", "30")] public void ParseVolumeTest(string filename, string expected) { Assert.Equal(expected, API.Services.Tasks.Scanner.Parser.Parser.ParseVolume(filename)); @@ -204,6 +205,7 @@ public class MangaParserTests [InlineData("죠시라쿠! 2년 후 1권", "죠시라쿠! 2년 후")] [InlineData("test 2 years 1권", "test 2 years")] [InlineData("test 2 years 1화", "test 2 years")] + [InlineData("Nagasarete Airantou - Vol. 30 Ch. 187.5 - Vol.30 Omake", "Nagasarete Airantou")] public void ParseSeriesTest(string filename, string expected) { Assert.Equal(expected, API.Services.Tasks.Scanner.Parser.Parser.ParseSeries(filename)); diff --git a/API.Tests/Services/ScrobblingServiceTests.cs b/API.Tests/Services/ScrobblingServiceTests.cs index 2d40c5211..d460ee4e5 100644 --- a/API.Tests/Services/ScrobblingServiceTests.cs +++ b/API.Tests/Services/ScrobblingServiceTests.cs @@ -2,13 +2,23 @@ using Xunit; namespace API.Tests.Services; +#nullable enable public class ScrobblingServiceTests { [Theory] [InlineData("https://anilist.co/manga/35851/Byeontaega-Doeja/", 35851)] - public void CanParseWeblink(string link, long expectedId) + [InlineData("https://anilist.co/manga/30105", 30105)] + [InlineData("https://anilist.co/manga/30105/Kekkaishi/", 30105)] + public void CanParseWeblink_AniList(string link, int? expectedId) { - Assert.Equal(ScrobblingService.ExtractId(link, ScrobblingService.AniListWeblinkWebsite), expectedId); + Assert.Equal(ScrobblingService.ExtractId(link, ScrobblingService.AniListWeblinkWebsite), expectedId); + } + + [Theory] + [InlineData("https://mangadex.org/title/316d3d09-bb83-49da-9d90-11dc7ce40967/honzuki-no-gekokujou-shisho-ni-naru-tame-ni-wa-shudan-wo-erandeiraremasen-dai-3-bu-ryouchi-ni-hon-o", "316d3d09-bb83-49da-9d90-11dc7ce40967")] + public void CanParseWeblink_MangaDex(string link, string expectedId) + { + Assert.Equal(ScrobblingService.ExtractId(link, ScrobblingService.MangaDexWeblinkWebsite), expectedId); } } diff --git a/API/Controllers/AccountController.cs b/API/Controllers/AccountController.cs index 576011b91..dce22c979 100644 --- a/API/Controllers/AccountController.cs +++ b/API/Controllers/AccountController.cs @@ -26,6 +26,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.RateLimiting; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; +using SharpCompress; namespace API.Controllers; @@ -137,8 +138,7 @@ public class AccountController : BaseApiController if (!result.Succeeded) return BadRequest(result.Errors); // Assign default streams - user.DashboardStreams = Seed.DefaultStreams.ToList(); - user.SideNavStreams = Seed.DefaultSideNavStreams.ToList(); + AddDefaultStreamsToUser(user); var token = await _userManager.GenerateEmailConfirmationTokenAsync(user); if (string.IsNullOrEmpty(token)) return BadRequest(await _localizationService.Get("en", "confirm-token-gen")); @@ -608,7 +608,8 @@ public class AccountController : BaseApiController } // Create a new user - var user = new AppUserBuilder(dto.Email, dto.Email, await _unitOfWork.SiteThemeRepository.GetDefaultTheme()).Build(); + var user = new AppUserBuilder(dto.Email, dto.Email, + await _unitOfWork.SiteThemeRepository.GetDefaultTheme()).Build(); _unitOfWork.UserRepository.Add(user); try { @@ -616,9 +617,7 @@ public class AccountController : BaseApiController if (!result.Succeeded) return BadRequest(result.Errors); // Assign default streams - user.DashboardStreams = Seed.DefaultStreams.ToList(); - user.SideNavStreams = Seed.DefaultSideNavStreams.ToList(); - + AddDefaultStreamsToUser(user); // Assign Roles var roles = dto.Roles; @@ -657,7 +656,6 @@ public class AccountController : BaseApiController user.CreateSideNavFromLibrary(lib); } - _unitOfWork.UserRepository.Update(user); user.AgeRestriction = hasAdminRole ? AgeRating.NotApplicable : dto.AgeRestriction.AgeRating; user.AgeRestrictionIncludeUnknowns = hasAdminRole || dto.AgeRestriction.IncludeUnknowns; @@ -669,6 +667,7 @@ public class AccountController : BaseApiController } user.ConfirmationToken = token; + _unitOfWork.UserRepository.Update(user); await _unitOfWork.CommitAsync(); } catch (Exception ex) @@ -702,7 +701,7 @@ public class AccountController : BaseApiController BackgroundJob.Enqueue(() => _emailService.SendConfirmationEmail(new ConfirmationEmailDto() { EmailAddress = dto.Email, - InvitingUser = adminUser.UserName!, + InvitingUser = adminUser.UserName, ServerConfirmationLink = emailLink })); } @@ -721,6 +720,19 @@ public class AccountController : BaseApiController return BadRequest(await _localizationService.Translate(User.GetUserId(), "generic-invite-user")); } + private void AddDefaultStreamsToUser(AppUser user) + { + foreach (var newStream in Seed.DefaultStreams.Select(stream => _mapper.Map(stream))) + { + user.DashboardStreams.Add(newStream); + } + + foreach (var stream in Seed.DefaultSideNavStreams.Select(stream => _mapper.Map(stream))) + { + user.SideNavStreams.Add(stream); + } + } + /// /// Last step in authentication flow, confirms the email token for email /// diff --git a/API/Controllers/ReaderController.cs b/API/Controllers/ReaderController.cs index dcee9b62d..89078487d 100644 --- a/API/Controllers/ReaderController.cs +++ b/API/Controllers/ReaderController.cs @@ -115,10 +115,6 @@ public class ReaderController : BaseApiController try { - if (new Random().Next(1, 10) > 5) - { - await Task.Delay(1000); - } var chapter = await _cacheService.Ensure(chapterId, extractPdf); if (chapter == null) return NoContent(); _logger.LogInformation("Fetching Page {PageNum} on Chapter {ChapterId}", page, chapterId); diff --git a/API/Data/Seed.cs b/API/Data/Seed.cs index b807e4dcd..c5fe643ea 100644 --- a/API/Data/Seed.cs +++ b/API/Data/Seed.cs @@ -76,48 +76,42 @@ public static class Seed }, }.ToArray()); - public static readonly ImmutableArray DefaultSideNavStreams = ImmutableArray.Create(new[] + public static readonly ImmutableArray DefaultSideNavStreams = ImmutableArray.Create( + new AppUserSideNavStream() { - new AppUserSideNavStream() - { - Name = "want-to-read", - StreamType = SideNavStreamType.WantToRead, - Order = 1, - IsProvided = true, - Visible = true - }, - new AppUserSideNavStream() - { - Name = "collections", - StreamType = SideNavStreamType.Collections, - Order = 2, - IsProvided = true, - Visible = true - }, - new AppUserSideNavStream() - { - Name = "reading-lists", - StreamType = SideNavStreamType.ReadingLists, - Order = 3, - IsProvided = true, - Visible = true - }, - new AppUserSideNavStream() - { - Name = "bookmarks", - StreamType = SideNavStreamType.Bookmarks, - Order = 4, - IsProvided = true, - Visible = true - }, - new AppUserSideNavStream() - { - Name = "all-series", - StreamType = SideNavStreamType.AllSeries, - Order = 5, - IsProvided = true, - Visible = true - } + Name = "want-to-read", + StreamType = SideNavStreamType.WantToRead, + Order = 1, + IsProvided = true, + Visible = true + }, new AppUserSideNavStream() + { + Name = "collections", + StreamType = SideNavStreamType.Collections, + Order = 2, + IsProvided = true, + Visible = true + }, new AppUserSideNavStream() + { + Name = "reading-lists", + StreamType = SideNavStreamType.ReadingLists, + Order = 3, + IsProvided = true, + Visible = true + }, new AppUserSideNavStream() + { + Name = "bookmarks", + StreamType = SideNavStreamType.Bookmarks, + Order = 4, + IsProvided = true, + Visible = true + }, new AppUserSideNavStream() + { + Name = "all-series", + StreamType = SideNavStreamType.AllSeries, + Order = 5, + IsProvided = true, + Visible = true }); diff --git a/API/Helpers/AutoMapperProfiles.cs b/API/Helpers/AutoMapperProfiles.cs index 77ab29396..8d0a4fa43 100644 --- a/API/Helpers/AutoMapperProfiles.cs +++ b/API/Helpers/AutoMapperProfiles.cs @@ -240,9 +240,10 @@ public class AutoMapperProfiles : Profile CreateMap(); CreateMap(); - // CreateMap() - // .ForMember(dest => dest.SmartFilterEncoded, - // opt => opt.MapFrom(src => src.SmartFilter)); + + // This is for cloning to ensure the records don't get overwritten when setting from SeedData + CreateMap(); + CreateMap(); } } diff --git a/API/Helpers/Builders/PlusSeriesDtoBuilder.cs b/API/Helpers/Builders/PlusSeriesDtoBuilder.cs index b379242ac..f1c43df84 100644 --- a/API/Helpers/Builders/PlusSeriesDtoBuilder.cs +++ b/API/Helpers/Builders/PlusSeriesDtoBuilder.cs @@ -27,6 +27,8 @@ public class PlusSeriesDtoBuilder : IEntityBuilder ScrobblingService.MalWeblinkWebsite), GoogleBooksId = ScrobblingService.ExtractId(series.Metadata.WebLinks, ScrobblingService.GoogleBooksWeblinkWebsite), + MangaDexId = ScrobblingService.ExtractId(series.Metadata.WebLinks, + ScrobblingService.MangaDexWeblinkWebsite), VolumeCount = series.Volumes.Count, ChapterCount = series.Volumes.SelectMany(v => v.Chapters).Count(c => !c.IsSpecial), Year = series.Metadata.ReleaseYear diff --git a/API/Helpers/SmartFilterHelper.cs b/API/Helpers/SmartFilterHelper.cs index 0749cb29e..4b8e0c8f6 100644 --- a/API/Helpers/SmartFilterHelper.cs +++ b/API/Helpers/SmartFilterHelper.cs @@ -133,7 +133,7 @@ public static class SmartFilterHelper var sortFieldPart = parts.FirstOrDefault(part => part.StartsWith(SortFieldKey)); var isAscendingPart = parts.FirstOrDefault(part => part.StartsWith(IsAscendingKey)); - var isAscending = isAscendingPart?.Substring(11).Equals("true", StringComparison.OrdinalIgnoreCase) ?? false; + var isAscending = isAscendingPart?.Trim().Replace(IsAscendingKey, string.Empty).Equals("true", StringComparison.OrdinalIgnoreCase) ?? false; if (sortFieldPart == null) { return new SortOptions(); diff --git a/API/Services/Plus/RecommendationService.cs b/API/Services/Plus/RecommendationService.cs index d5dd67231..1a6a1b315 100644 --- a/API/Services/Plus/RecommendationService.cs +++ b/API/Services/Plus/RecommendationService.cs @@ -25,6 +25,7 @@ public record PlusSeriesDto public int? AniListId { get; set; } public long? MalId { get; set; } public string? GoogleBooksId { get; set; } + public string? MangaDexId { get; set; } public string SeriesName { get; set; } public string? AltSeriesName { get; set; } public MediaFormat MediaFormat { get; set; } diff --git a/API/Services/Plus/ScrobblingService.cs b/API/Services/Plus/ScrobblingService.cs index 4f679fb2b..d33ccfb19 100644 --- a/API/Services/Plus/ScrobblingService.cs +++ b/API/Services/Plus/ScrobblingService.cs @@ -67,13 +67,14 @@ public class ScrobblingService : IScrobblingService public const string AniListWeblinkWebsite = "https://anilist.co/manga/"; public const string MalWeblinkWebsite = "https://myanimelist.net/manga/"; public const string GoogleBooksWeblinkWebsite = "https://books.google.com/books?id="; + public const string MangaDexWeblinkWebsite = "https://mangadex.org/title/"; private static readonly IDictionary WeblinkExtractionMap = new Dictionary() { {AniListWeblinkWebsite, 0}, {MalWeblinkWebsite, 0}, {GoogleBooksWeblinkWebsite, 0}, - + {MangaDexWeblinkWebsite, 0}, }; private const int ScrobbleSleepTime = 700; // We can likely tie this to AniList's 90 rate / min ((60 * 1000) / 90) @@ -829,12 +830,12 @@ public class ScrobblingService : IScrobblingService if (!webLink.StartsWith(website)) continue; var tokens = webLink.Split(website)[1].Split('/'); var value = tokens[index]; - if (typeof(T) == typeof(int)) + if (typeof(T) == typeof(int?)) { if (int.TryParse(value, out var intValue)) return (T)(object)intValue; } - else if (typeof(T) == typeof(long)) + else if (typeof(T) == typeof(long?)) { if (long.TryParse(value, out var longValue)) return (T)(object)longValue; diff --git a/API/Services/TaskScheduler.cs b/API/Services/TaskScheduler.cs index a7a309844..14f24b30e 100644 --- a/API/Services/TaskScheduler.cs +++ b/API/Services/TaskScheduler.cs @@ -34,7 +34,6 @@ public interface ITaskScheduler void ScanSiteThemes(); void CovertAllCoversToEncoding(); Task CleanupDbEntries(); - Task ScrobbleUpdates(int userId); } public class TaskScheduler : ITaskScheduler @@ -141,7 +140,6 @@ public class TaskScheduler : ITaskScheduler } RecurringJob.AddOrUpdate(CleanupTaskId, () => _cleanupService.Cleanup(), Cron.Daily, RecurringJobOptions); - RecurringJob.AddOrUpdate(CleanupDbTaskId, () => _cleanupService.CleanupDbEntries(), Cron.Daily, RecurringJobOptions); RecurringJob.AddOrUpdate(RemoveFromWantToReadTaskId, () => _cleanupService.CleanupWantToRead(), Cron.Daily, RecurringJobOptions); RecurringJob.AddOrUpdate(UpdateYearlyStatsTaskId, () => _statisticService.UpdateServerStatistics(), Cron.Monthly, RecurringJobOptions); @@ -272,16 +270,6 @@ public class TaskScheduler : ITaskScheduler await _cleanupService.CleanupDbEntries(); } - /// - /// TODO: Remove this for Release - /// - /// - public async Task ScrobbleUpdates(int userId) - { - if (!await _licenseService.HasActiveLicense()) return; - BackgroundJob.Enqueue(() => _scrobblingService.ProcessUpdatesSinceLastSync()); - } - /// /// Attempts to call ScanLibraries on ScannerService, but if another scan task is in progress, will reschedule the invocation for 3 hours in future. /// diff --git a/API/Services/Tasks/BackupService.cs b/API/Services/Tasks/BackupService.cs index 99e921c50..a95b9f108 100644 --- a/API/Services/Tasks/BackupService.cs +++ b/API/Services/Tasks/BackupService.cs @@ -9,6 +9,7 @@ using API.Entities.Enums; using API.Logging; using API.SignalR; using Hangfire; +using Kavita.Common.EnvironmentInfo; using Microsoft.Extensions.Logging; namespace API.Services.Tasks; @@ -91,7 +92,7 @@ public class BackupService : IBackupService await SendProgress(0.1F, "Copying core files"); var dateString = $"{DateTime.UtcNow.ToShortDateString()}_{DateTime.UtcNow.ToLongTimeString()}".Replace("/", "_").Replace(":", "_"); - var zipPath = _directoryService.FileSystem.Path.Join(backupDirectory, $"kavita_backup_{dateString}.zip"); + var zipPath = _directoryService.FileSystem.Path.Join(backupDirectory, $"kavita_backup_{dateString}_v{BuildInfo.Version}.zip"); if (File.Exists(zipPath)) { diff --git a/API/Services/Tasks/CleanupService.cs b/API/Services/Tasks/CleanupService.cs index c5b908f73..3d657a929 100644 --- a/API/Services/Tasks/CleanupService.cs +++ b/API/Services/Tasks/CleanupService.cs @@ -92,6 +92,8 @@ public class CleanupService : ICleanupService await CleanupLogs(); await SendProgress(0.9F, "Cleaning progress events that exceed 100%"); await EnsureChapterProgressIsCapped(); + await SendProgress(0.95F, "Cleaning abandoned database rows"); + await CleanupDbEntries(); await SendProgress(1F, "Cleanup finished"); _logger.LogInformation("Cleanup finished"); } diff --git a/API/Services/Tasks/Scanner/Parser/Parser.cs b/API/Services/Tasks/Scanner/Parser/Parser.cs index 8753cd2ca..0b71ec67b 100644 --- a/API/Services/Tasks/Scanner/Parser/Parser.cs +++ b/API/Services/Tasks/Scanner/Parser/Parser.cs @@ -109,9 +109,9 @@ public static class Parser new Regex( @"(?.*)(\b|_)v(?\d+-?\d+)( |_)", MatchOptions, RegexTimeout), - // NEEDLESS_Vol.4_-Simeon_6_v2[SugoiSugoi].rar + // Nagasarete Airantou - Vol. 30 Ch. 187.5 - Vol.31 Omake new Regex( - @"(?.*)(\b|_)(?!\[)(vol\.?)(?\d+(-\d+)?)(?!\])", + @"^(?.+?)(\s*Chapter\s*\d+)?(\s|_|\-\s)+(Vol(ume)?\.?(\s|_)?)(?\d+(\.\d+)?)(.+?|$)", MatchOptions, RegexTimeout), // Historys Strongest Disciple Kenichi_v11_c90-98.zip or Dance in the Vampire Bund v16-17 new Regex( @@ -137,6 +137,7 @@ public static class Parser new Regex( @"(vol_)(?\d+(\.\d)?)", MatchOptions, RegexTimeout), + // Chinese Volume: 第n卷 -> Volume n, 第n册 -> Volume n, 幽游白书完全版 第03卷 天下 or 阿衰online 第1册 new Regex( @"第(?\d+)(卷|册)", @@ -197,16 +198,17 @@ public static class Parser new Regex( @"(?.*)(\b|_|-|\s)(?:sp)\d", MatchOptions, RegexTimeout), - // [SugoiSugoi]_NEEDLESS_Vol.2_-_Disk_The_Informant_5_[ENG].rar, Yuusha Ga Shinda! - Vol.tbd Chapter 27.001 V2 Infection ①.cbz - new Regex( - @"^(?.*)( |_)Vol\.?(\d+|tbd)", - MatchOptions, RegexTimeout), // Mad Chimera World - Volume 005 - Chapter 026.cbz (couldn't figure out how to get Volume negative lookaround working on below regex), // The Duke of Death and His Black Maid - Vol. 04 Ch. 054.5 - V4 Omake new Regex( @"(?.+?)(\s|_|-)+(?:Vol(ume|\.)?(\s|_|-)+\d+)(\s|_|-)+(?:(Ch|Chapter|Ch)\.?)(\s|_|-)+(?\d+)", MatchOptions, RegexTimeout), + // [SugoiSugoi]_NEEDLESS_Vol.2_-_Disk_The_Informant_5_[ENG].rar, Yuusha Ga Shinda! - Vol.tbd Chapter 27.001 V2 Infection ①.cbz, + // Nagasarete Airantou - Vol. 30 Ch. 187.5 - Vol.30 Omake + new Regex( + @"^(?.+?)(\s*Chapter\s*\d+)?(\s|_|\-\s)+Vol(ume)?\.?(\d+|tbd|\s\d).+?", + MatchOptions, RegexTimeout), // Ichiban_Ushiro_no_Daimaou_v04_ch34_[VISCANS].zip, VanDread-v01-c01.zip new Regex( @"(?.*)(\b|_)v(?\d+-?\d*)(\s|_|-)", @@ -233,6 +235,7 @@ public static class Parser @"(?.+?):?(\s|\b|_|-)Chapter(\s|\b|_|-)\d+(\s|\b|_|-)(vol)(ume)", MatchOptions, RegexTimeout), + // [xPearse] Kyochuu Rettou Volume 1 [English] [Manga] [Volume Scans] new Regex( @"(?.+?):? (\b|_|-)(vol)(ume)", diff --git a/API/config/config.7z b/API/config/config.7z deleted file mode 100644 index 7415012b2..000000000 Binary files a/API/config/config.7z and /dev/null differ diff --git a/README.md b/README.md index 5cbd43675..5380a52bf 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ install methods and platforms. **Note: Kavita is under heavy development and is being updated all the time, so the tag for bleeding edge builds is `:nightly`. The `:latest` tag will be the latest stable release.** ## Feature Requests -Got a great idea? Throw it up on our [Feature Request site](https://feats.kavitareader.com/) or vote on another idea. Please check the [Project Board](https://github.com/Kareadita/Kavita/projects) first for a list of planned features before you submit an idea. +Got a great idea? Throw it up on our [Feature Request site](https://feats.kavitareader.com/) or vote on another idea. Please check the [Project Board](https://github.com/Kareadita/Kavita/projects?type=classic) first for a list of planned features before you submit an idea. ## Notice Kavita is being actively developed and should be considered beta software until the 1.0 release. diff --git a/UI/Web/src/app/_pipes/utc-to-local-time.pipe.ts b/UI/Web/src/app/_pipes/utc-to-local-time.pipe.ts index a9be8e300..22c8c4639 100644 --- a/UI/Web/src/app/_pipes/utc-to-local-time.pipe.ts +++ b/UI/Web/src/app/_pipes/utc-to-local-time.pipe.ts @@ -15,7 +15,8 @@ type UtcToLocalTimeFormat = 'full' | 'short' | 'shortDate' | 'shortTime'; }) export class UtcToLocalTimePipe implements PipeTransform { - transform(utcDate: string, format: UtcToLocalTimeFormat = 'short'): string { + transform(utcDate: string | undefined | null, format: UtcToLocalTimeFormat = 'short'): string { + if (utcDate === undefined || utcDate === null) return ''; const browserLanguage = navigator.language; const dateTime = DateTime.fromISO(utcDate, { zone: 'utc' }).toLocal().setLocale(browserLanguage); diff --git a/UI/Web/src/app/admin/manage-tasks-settings/manage-tasks-settings.component.html b/UI/Web/src/app/admin/manage-tasks-settings/manage-tasks-settings.component.html index 76017a435..5816a1c28 100644 --- a/UI/Web/src/app/admin/manage-tasks-settings/manage-tasks-settings.component.html +++ b/UI/Web/src/app/admin/manage-tasks-settings/manage-tasks-settings.component.html @@ -58,7 +58,9 @@ {{task.title | titlecase}} - {{task.lastExecutionUtc | utcToLocalTime | defaultValue }} + + {{task.lastExecutionUtc | utcToLocalTime | defaultValue }} + {{task.cron}} diff --git a/UI/Web/src/app/manga-reader/_components/manga-reader/manga-reader.component.html b/UI/Web/src/app/manga-reader/_components/manga-reader/manga-reader.component.html index 29fd55940..85d20f1cf 100644 --- a/UI/Web/src/app/manga-reader/_components/manga-reader/manga-reader.component.html +++ b/UI/Web/src/app/manga-reader/_components/manga-reader/manga-reader.component.html @@ -36,7 +36,7 @@ - +
diff --git a/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.html b/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.html index 267f5e09b..7d1976340 100644 --- a/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.html +++ b/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.html @@ -51,7 +51,7 @@
-
+
diff --git a/openapi.json b/openapi.json index 3fb292b83..377e11e1e 100644 --- a/openapi.json +++ b/openapi.json @@ -7,7 +7,7 @@ "name": "GPL-3.0", "url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE" }, - "version": "0.7.11.0" + "version": "0.7.11.1" }, "servers": [ {