diff --git a/API/Controllers/DownloadController.cs b/API/Controllers/DownloadController.cs index 27a7b59ab..5a249c9a8 100644 --- a/API/Controllers/DownloadController.cs +++ b/API/Controllers/DownloadController.cs @@ -158,6 +158,7 @@ public class DownloadController : BaseApiController await _eventHub.SendMessageAsync(MessageFactory.NotificationProgress, MessageFactory.DownloadProgressEvent(username, filename, $"Downloading {filename}", 0F, "started")); + if (files.Count == 1 && files.First().Format != MangaFormat.Image) { await _eventHub.SendMessageAsync(MessageFactory.NotificationProgress, @@ -167,15 +168,17 @@ public class DownloadController : BaseApiController } var filePath = _archiveService.CreateZipFromFoldersForDownload(files.Select(c => c.FilePath).ToList(), tempFolder, ProgressCallback); + await _eventHub.SendMessageAsync(MessageFactory.NotificationProgress, MessageFactory.DownloadProgressEvent(username, filename, "Download Complete", 1F, "ended")); + return PhysicalFile(filePath, DefaultContentType, Uri.EscapeDataString(downloadName), true); async Task ProgressCallback(Tuple progressInfo) { await _eventHub.SendMessageAsync(MessageFactory.NotificationProgress, - MessageFactory.DownloadProgressEvent(username, filename, $"Extracting {Path.GetFileNameWithoutExtension(progressInfo.Item1)}", + MessageFactory.DownloadProgressEvent(username, filename, $"Processing {Path.GetFileNameWithoutExtension(progressInfo.Item1)}", Math.Clamp(progressInfo.Item2, 0F, 1F))); } } @@ -193,8 +196,10 @@ public class DownloadController : BaseApiController public async Task DownloadSeries(int seriesId) { if (!await HasDownloadPermission()) return BadRequest(await _localizationService.Translate(User.GetUserId(), "permission-denied")); + var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId); if (series == null) return BadRequest("Invalid Series"); + var files = await _unitOfWork.SeriesRepository.GetFilesForSeries(seriesId); try { diff --git a/API/Controllers/OPDSController.cs b/API/Controllers/OPDSController.cs index ee8972327..fcc4ca58f 100644 --- a/API/Controllers/OPDSController.cs +++ b/API/Controllers/OPDSController.cs @@ -595,13 +595,7 @@ public class OpdsController : BaseApiController return Unauthorized(); } - var readingLists = await _unitOfWork.ReadingListRepository.GetReadingListDtosForUserAsync(user.Id, true, GetUserParams(pageNumber), false); - if (readingLists == null) - { - return Unauthorized(); - } - - var readingList = readingLists.FirstOrDefault(rl => rl.Id == readingListId); + var readingList = await _unitOfWork.ReadingListRepository.GetReadingListDtoByIdAsync(readingListId, user.Id); if (readingList == null) { return BadRequest(await _localizationService.Translate(userId, "reading-list-restricted")); diff --git a/API/Controllers/ReadingListController.cs b/API/Controllers/ReadingListController.cs index 25010d636..e4233e36f 100644 --- a/API/Controllers/ReadingListController.cs +++ b/API/Controllers/ReadingListController.cs @@ -39,9 +39,15 @@ public class ReadingListController : BaseApiController /// /// [HttpGet] - public async Task>> GetList(int readingListId) + public async Task> GetList(int readingListId) { - return Ok(await _unitOfWork.ReadingListRepository.GetReadingListDtoByIdAsync(readingListId, User.GetUserId())); + var readingList = await _unitOfWork.ReadingListRepository.GetReadingListDtoByIdAsync(readingListId, User.GetUserId()); + if (readingList == null) + { + return BadRequest(await _localizationService.Translate(User.GetUserId(), "reading-list-restricted")); + } + + return Ok(readingList); } /// diff --git a/API/Data/Repositories/ReadingListRepository.cs b/API/Data/Repositories/ReadingListRepository.cs index 3b75dc10f..3d43c533e 100644 --- a/API/Data/Repositories/ReadingListRepository.cs +++ b/API/Data/Repositories/ReadingListRepository.cs @@ -351,8 +351,10 @@ public class ReadingListRepository : IReadingListRepository public async Task GetReadingListDtoByIdAsync(int readingListId, int userId) { + var user = await _context.AppUser.FirstAsync(u => u.Id == userId); return await _context.ReadingList .Where(r => r.Id == readingListId && (r.AppUserId == userId || r.Promoted)) + .RestrictAgainstAgeRestriction(user.GetAgeRestriction()) .ProjectTo(_mapper.ConfigurationProvider) .SingleOrDefaultAsync(); } diff --git a/API/Services/AccountService.cs b/API/Services/AccountService.cs index ff8e12592..74b6709fa 100644 --- a/API/Services/AccountService.cs +++ b/API/Services/AccountService.cs @@ -8,6 +8,7 @@ using API.Data; using API.DTOs.Account; using API.Entities; using API.Errors; +using API.Extensions; using Kavita.Common; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; @@ -78,8 +79,9 @@ public class AccountService : IAccountService } public async Task> ValidateUsername(string username) { + // Reverted because of https://go.microsoft.com/fwlink/?linkid=2129535 if (await _userManager.Users.AnyAsync(x => x.NormalizedUserName != null - && x.NormalizedUserName.Equals(username, StringComparison.CurrentCultureIgnoreCase))) + && x.NormalizedUserName == username.ToUpper())) { return [ diff --git a/API/Services/ArchiveService.cs b/API/Services/ArchiveService.cs index f8d1a9411..335a5a74b 100644 --- a/API/Services/ArchiveService.cs +++ b/API/Services/ArchiveService.cs @@ -363,16 +363,15 @@ public class ArchiveService : IArchiveService tempPath = Path.Join(tempLocation, parentDirectory ?? _directoryService.FileSystem.FileInfo.New(path).Name); } - progressCallback(Tuple.Create(_directoryService.FileSystem.FileInfo.New(path).Name, (1.0f * totalFiles) / count)); if (Tasks.Scanner.Parser.Parser.IsArchive(path)) { - ExtractArchive(path, tempPath); - } - else - { - _directoryService.CopyFileToDirectory(path, tempPath); + // Archives don't need to be put into a subdirectory of the same name + tempPath = _directoryService.GetParentDirectoryName(tempPath); } + progressCallback(Tuple.Create(_directoryService.FileSystem.FileInfo.New(path).Name, (1.0f * totalFiles) / count)); + + _directoryService.CopyFileToDirectory(path, tempPath); count++; } } diff --git a/UI/Web/src/app/_pipes/time-ago.pipe.ts b/UI/Web/src/app/_pipes/time-ago.pipe.ts index 99039126c..5780b18ab 100644 --- a/UI/Web/src/app/_pipes/time-ago.pipe.ts +++ b/UI/Web/src/app/_pipes/time-ago.pipe.ts @@ -39,9 +39,8 @@ export class TimeAgoPipe implements PipeTransform, OnDestroy { constructor(private readonly changeDetectorRef: ChangeDetectorRef, private ngZone: NgZone, private translocoService: TranslocoService) {} - transform(value: string) { - - if (value === '' || value === null || value === undefined || value.split('T')[0] === '0001-01-01') { + transform(value: string | Date | null) { + if (value === '' || value === null || value === undefined || (value instanceof String && value.split('T')[0] === '0001-01-01')) { return this.translocoService.translate('time-ago-pipe.never'); } diff --git a/UI/Web/src/app/_pipes/utc-to-locale-date.pipe.ts b/UI/Web/src/app/_pipes/utc-to-locale-date.pipe.ts new file mode 100644 index 000000000..0a25eefdc --- /dev/null +++ b/UI/Web/src/app/_pipes/utc-to-locale-date.pipe.ts @@ -0,0 +1,24 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import {DateTime} from "luxon"; + +@Pipe({ + name: 'utcToLocaleDate', + standalone: true +}) +/** + * This is the same as the UtcToLocalTimePipe but returning a timezone aware DateTime object rather than a string. + * Use this when the next operation needs a Date object (like the TimeAgoPipe) + */ +export class UtcToLocaleDatePipe implements PipeTransform { + + transform(utcDate: string | undefined | null): Date | null { + if (utcDate === '' || utcDate === null || utcDate === undefined || utcDate.split('T')[0] === '0001-01-01') { + return null; + } + + const browserLanguage = navigator.language; + const dateTime = DateTime.fromISO(utcDate, { zone: 'utc' }).toLocal().setLocale(browserLanguage); + return dateTime.toJSDate() + } + +} diff --git a/UI/Web/src/app/_single-module/edit-chapter-modal/edit-chapter-modal.component.html b/UI/Web/src/app/_single-module/edit-chapter-modal/edit-chapter-modal.component.html index b0a419fe1..6520e2f8a 100644 --- a/UI/Web/src/app/_single-module/edit-chapter-modal/edit-chapter-modal.component.html +++ b/UI/Web/src/app/_single-module/edit-chapter-modal/edit-chapter-modal.component.html @@ -535,7 +535,7 @@
- {{chapter.createdUtc | utcToLocalTime | translocoDate: {dateStyle: 'short', timeStyle: 'short' } | defaultDate}} + {{chapter.createdUtc | utcToLocalTime:'short' | defaultDate}}
diff --git a/UI/Web/src/app/_single-module/edit-chapter-modal/edit-chapter-modal.component.ts b/UI/Web/src/app/_single-module/edit-chapter-modal/edit-chapter-modal.component.ts index 812ea6eb9..6e0a8915b 100644 --- a/UI/Web/src/app/_single-module/edit-chapter-modal/edit-chapter-modal.component.ts +++ b/UI/Web/src/app/_single-module/edit-chapter-modal/edit-chapter-modal.component.ts @@ -197,7 +197,12 @@ export class EditChapterModalComponent implements OnInit { this.editForm.addControl('language', new FormControl(this.chapter.language, [])); this.editForm.addControl('isbn', new FormControl(this.chapter.isbn, [])); this.editForm.addControl('ageRating', new FormControl(this.chapter.ageRating, [])); - this.editForm.addControl('releaseDate', new FormControl(this.chapter.releaseDate, [])); + + if (this.chapter.releaseDate !== '0001-01-01T00:00:00') { + this.editForm.addControl('releaseDate', new FormControl(this.chapter.releaseDate.substring(0, 10), [])); + } else { + this.editForm.addControl('releaseDate', new FormControl('', [])); + } this.editForm.addControl('genres', new FormControl(this.chapter.genres, [])); @@ -261,7 +266,11 @@ export class EditChapterModalComponent implements OnInit { const model = this.editForm.value; const selectedIndex = this.editForm.get('coverImageIndex')?.value || 0; - this.chapter.releaseDate = model.releaseDate; + if (model.releaseDate === '') { + this.chapter.releaseDate = '0001-01-01T00:00:00'; + } else { + this.chapter.releaseDate = model.releaseDate + 'T00:00:00'; + } this.chapter.ageRating = parseInt(model.ageRating + '', 10) as AgeRating; this.chapter.genres = model.genres; this.chapter.tags = model.tags; diff --git a/UI/Web/src/app/_single-module/edit-volume-modal/edit-volume-modal.component.html b/UI/Web/src/app/_single-module/edit-volume-modal/edit-volume-modal.component.html index b42f4cf45..b0f331b51 100644 --- a/UI/Web/src/app/_single-module/edit-volume-modal/edit-volume-modal.component.html +++ b/UI/Web/src/app/_single-module/edit-volume-modal/edit-volume-modal.component.html @@ -60,7 +60,7 @@
- {{volume.createdUtc | utcToLocalTime | translocoDate: {dateStyle: 'short', timeStyle: 'short' } | defaultDate}} + {{volume.createdUtc | utcToLocalTime:'short' | defaultDate}}
diff --git a/UI/Web/src/app/_single-module/smart-collection-drawer/smart-collection-drawer.component.html b/UI/Web/src/app/_single-module/smart-collection-drawer/smart-collection-drawer.component.html index 7634c9c12..525155e6a 100644 --- a/UI/Web/src/app/_single-module/smart-collection-drawer/smart-collection-drawer.component.html +++ b/UI/Web/src/app/_single-module/smart-collection-drawer/smart-collection-drawer.component.html @@ -12,7 +12,7 @@ - {{collection.lastSyncUtc | utcToLocalTime | date:'shortDate' | defaultDate}} + {{collection.lastSyncUtc | utcToLocalTime:'shortDate' | defaultDate}} diff --git a/UI/Web/src/app/admin/manage-users/manage-users.component.html b/UI/Web/src/app/admin/manage-users/manage-users.component.html index 018c9d56a..0c8aa4639 100644 --- a/UI/Web/src/app/admin/manage-users/manage-users.component.html +++ b/UI/Web/src/app/admin/manage-users/manage-users.component.html @@ -31,7 +31,7 @@ @if ((messageHub.onlineUsers$ | async)?.includes(member.username)) { {{t('online-now-tooltip')}} } @else { - {{member.lastActiveUtc | utcToLocalTime | timeAgo | sentenceCase | defaultDate}} + {{member.lastActiveUtc | utcToLocaleDate | timeAgo | sentenceCase | defaultDate}} } diff --git a/UI/Web/src/app/admin/manage-users/manage-users.component.ts b/UI/Web/src/app/admin/manage-users/manage-users.component.ts index ad60e391a..0a65395be 100644 --- a/UI/Web/src/app/admin/manage-users/manage-users.component.ts +++ b/UI/Web/src/app/admin/manage-users/manage-users.component.ts @@ -23,6 +23,7 @@ import {LoadingComponent} from "../../shared/loading/loading.component"; import {TimeAgoPipe} from "../../_pipes/time-ago.pipe"; import {SentenceCasePipe} from "../../_pipes/sentence-case.pipe"; import {DefaultModalOptions} from "../../_models/default-modal-options"; +import {UtcToLocaleDatePipe} from "../../_pipes/utc-to-locale-date.pipe"; @Component({ selector: 'app-manage-users', @@ -30,7 +31,7 @@ import {DefaultModalOptions} from "../../_models/default-modal-options"; styleUrls: ['./manage-users.component.scss'], standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, - imports: [NgbTooltip, TagBadgeComponent, AsyncPipe, TitleCasePipe, DatePipe, TranslocoModule, DefaultDatePipe, NgClass, DefaultValuePipe, ReadMoreComponent, UtcToLocalTimePipe, LoadingComponent, NgIf, TimeAgoPipe, SentenceCasePipe] + imports: [NgbTooltip, TagBadgeComponent, AsyncPipe, TitleCasePipe, DatePipe, TranslocoModule, DefaultDatePipe, NgClass, DefaultValuePipe, ReadMoreComponent, UtcToLocalTimePipe, LoadingComponent, NgIf, TimeAgoPipe, SentenceCasePipe, UtcToLocaleDatePipe] }) export class ManageUsersComponent implements OnInit { diff --git a/UI/Web/src/app/cards/_modals/edit-collection-tags/edit-collection-tags.component.html b/UI/Web/src/app/cards/_modals/edit-collection-tags/edit-collection-tags.component.html index fd90cc8a0..e16cdff78 100644 --- a/UI/Web/src/app/cards/_modals/edit-collection-tags/edit-collection-tags.component.html +++ b/UI/Web/src/app/cards/_modals/edit-collection-tags/edit-collection-tags.component.html @@ -112,7 +112,7 @@
{{t('last-sync-title')}}
-
{{tag.lastSyncUtc | utcToLocalTime | date:'shortDate' | defaultDate}}
+
{{tag.lastSyncUtc | utcToLocalTime:'shortDate' | defaultDate}}
{{t('source-url-title')}}
diff --git a/UI/Web/src/app/cards/_modals/edit-series-modal/edit-series-modal.component.html b/UI/Web/src/app/cards/_modals/edit-series-modal/edit-series-modal.component.html index 83012fd00..d8e47ed57 100644 --- a/UI/Web/src/app/cards/_modals/edit-series-modal/edit-series-modal.component.html +++ b/UI/Web/src/app/cards/_modals/edit-series-modal/edit-series-modal.component.html @@ -589,7 +589,7 @@
- {{series.created | date:'shortDate'}} + {{series.created | utcToLocalTime:'shortDate'}}
@@ -665,7 +665,7 @@ {{t('added-title')}} {{volume.createdUtc | utcToLocalTime | defaultDate}}
- {{t('last-modified-title')}} {{volume.lastModifiedUtc | utcToLocalTime | translocoDate: {dateStyle: 'short' } | defaultDate}} + {{t('last-modified-title')}} {{volume.lastModifiedUtc | utcToLocalTime:'short' | defaultDate}}
diff --git a/UI/Web/src/app/cards/edit-chapter-progress/edit-chapter-progress.component.html b/UI/Web/src/app/cards/edit-chapter-progress/edit-chapter-progress.component.html index 026c612c3..22f9d9150 100644 --- a/UI/Web/src/app/cards/edit-chapter-progress/edit-chapter-progress.component.html +++ b/UI/Web/src/app/cards/edit-chapter-progress/edit-chapter-progress.component.html @@ -74,10 +74,10 @@ } - {{progressEvents[idx].createdUtc | utcToLocalTime | date:'shortDate' | defaultDate}} + {{progressEvents[idx].createdUtc | utcToLocalTime:'shortDate' | defaultDate}} - {{progressEvents[idx].lastModifiedUtc | utcToLocalTime | date:'shortDate' | defaultDate}} + {{progressEvents[idx].lastModifiedUtc | utcToLocalTime:'shortDate' | defaultDate}}