diff --git a/API/Services/BookService.cs b/API/Services/BookService.cs
index 71f633e4c..cb31f2cc8 100644
--- a/API/Services/BookService.cs
+++ b/API/Services/BookService.cs
@@ -371,7 +371,7 @@ namespace API.Services
FullFilePath = filePath,
IsSpecial = false,
Series = series.Trim(),
- Volumes = seriesIndex.Split(".")[0]
+ Volumes = seriesIndex
};
}
}
diff --git a/API/Services/DirectoryService.cs b/API/Services/DirectoryService.cs
index 1a067a706..12011123d 100644
--- a/API/Services/DirectoryService.cs
+++ b/API/Services/DirectoryService.cs
@@ -173,7 +173,15 @@ namespace API.Services
return true;
}
-
+ ///
+ /// Checks if the root path of a path exists or not.
+ ///
+ ///
+ ///
+ public static bool IsDriveMounted(string path)
+ {
+ return new DirectoryInfo(Path.GetPathRoot(path) ?? string.Empty).Exists;
+ }
public static string[] GetFilesWithExtension(string path, string searchPatternExpression = "")
{
diff --git a/API/Services/Tasks/ScannerService.cs b/API/Services/Tasks/ScannerService.cs
index d82c9579c..38497aac2 100644
--- a/API/Services/Tasks/ScannerService.cs
+++ b/API/Services/Tasks/ScannerService.cs
@@ -56,6 +56,14 @@ namespace API.Services.Tasks
var chapterIds = await _unitOfWork.SeriesRepository.GetChapterIdsForSeriesAsync(new[] {seriesId});
var library = await _unitOfWork.LibraryRepository.GetLibraryForIdAsync(libraryId, LibraryIncludes.Folders);
var folderPaths = library.Folders.Select(f => f.Path).ToList();
+
+ // Check if any of the folder roots are not available (ie disconnected from network, etc) and fail if any of them are
+ if (folderPaths.Any(f => !DirectoryService.IsDriveMounted(f)))
+ {
+ _logger.LogError("Some of the root folders for library are not accessible. Please check that drives are connected and rescan. Scan will be aborted");
+ return;
+ }
+
var dirs = DirectoryService.FindHighestDirectoriesFromFiles(folderPaths, files.Select(f => f.FilePath).ToList());
_logger.LogInformation("Beginning file scan on {SeriesName}", series.Name);
@@ -195,6 +203,14 @@ namespace API.Services.Tasks
return;
}
+ // Check if any of the folder roots are not available (ie disconnected from network, etc) and fail if any of them are
+ if (library.Folders.Any(f => !DirectoryService.IsDriveMounted(f.Path)))
+ {
+ _logger.LogError("Some of the root folders for library are not accessible. Please check that drives are connected and rescan. Scan will be aborted");
+ return;
+ }
+
+
_logger.LogInformation("[ScannerService] Beginning file scan on {LibraryName}", library.Name);
await _messageHub.Clients.All.SendAsync(SignalREvents.ScanLibraryProgress,
MessageFactory.ScanLibraryProgressEvent(libraryId, 0));
diff --git a/UI/Web/src/app/_models/events/series-removed-event.ts b/UI/Web/src/app/_models/events/series-removed-event.ts
new file mode 100644
index 000000000..9901fc7e5
--- /dev/null
+++ b/UI/Web/src/app/_models/events/series-removed-event.ts
@@ -0,0 +1,5 @@
+export interface SeriesRemovedEvent {
+ libraryId: number;
+ seriesId: number;
+ seriesName: string;
+}
\ No newline at end of file
diff --git a/UI/Web/src/app/_services/message-hub.service.ts b/UI/Web/src/app/_services/message-hub.service.ts
index c058e1596..ba62adb49 100644
--- a/UI/Web/src/app/_services/message-hub.service.ts
+++ b/UI/Web/src/app/_services/message-hub.service.ts
@@ -16,6 +16,7 @@ export enum EVENTS {
ScanSeries = 'ScanSeries',
RefreshMetadata = 'RefreshMetadata',
SeriesAdded = 'SeriesAdded',
+ SeriesRemoved = 'SeriesRemoved',
ScanLibraryProgress = 'ScanLibraryProgress',
OnlineUsers = 'OnlineUsers',
SeriesAddedToCollection = 'SeriesAddedToCollection',
@@ -115,6 +116,13 @@ export class MessageHubService {
}
});
+ this.hubConnection.on(EVENTS.SeriesRemoved, resp => {
+ this.messagesSource.next({
+ event: EVENTS.SeriesRemoved,
+ payload: resp.body
+ });
+ });
+
this.hubConnection.on(EVENTS.RefreshMetadata, resp => {
this.messagesSource.next({
event: EVENTS.RefreshMetadata,
diff --git a/UI/Web/src/app/cards/_modals/card-details-modal/card-details-modal.component.html b/UI/Web/src/app/cards/_modals/card-details-modal/card-details-modal.component.html
index b5938118c..62a40f9e0 100644
--- a/UI/Web/src/app/cards/_modals/card-details-modal/card-details-modal.component.html
+++ b/UI/Web/src/app/cards/_modals/card-details-modal/card-details-modal.component.html
@@ -17,7 +17,8 @@
Id: {{data.id}}
-
+
+ Format: {{utilityService.mangaFormat(series.format) | sentenceCase}}
@@ -58,8 +59,8 @@
Pages: {{file.pages}}
-
- Format:
{{utilityService.mangaFormatToText(file.format)}}
+
+ Added: {{(data.created | date: 'short') || '-'}}
diff --git a/UI/Web/src/app/cards/_modals/card-details-modal/card-details-modal.component.ts b/UI/Web/src/app/cards/_modals/card-details-modal/card-details-modal.component.ts
index 3f15edd65..e1501cd9b 100644
--- a/UI/Web/src/app/cards/_modals/card-details-modal/card-details-modal.component.ts
+++ b/UI/Web/src/app/cards/_modals/card-details-modal/card-details-modal.component.ts
@@ -15,6 +15,8 @@ import { UploadService } from 'src/app/_services/upload.service';
import { ChangeCoverImageModalComponent } from '../change-cover-image/change-cover-image-modal.component';
import { LibraryType } from '../../../_models/library';
import { LibraryService } from '../../../_services/library.service';
+import { SeriesService } from 'src/app/_services/series.service';
+import { Series } from 'src/app/_models/series';
@@ -42,6 +44,7 @@ export class CardDetailsModalComponent implements OnInit {
actions: ActionItem
[] = [];
chapterActions: ActionItem[] = [];
libraryType: LibraryType = LibraryType.Manga;
+ series: Series | undefined = undefined;
get LibraryType(): typeof LibraryType {
return LibraryType;
@@ -50,7 +53,8 @@ export class CardDetailsModalComponent implements OnInit {
constructor(private modalService: NgbModal, public modal: NgbActiveModal, public utilityService: UtilityService,
public imageService: ImageService, private uploadService: UploadService, private toastr: ToastrService,
private accountService: AccountService, private actionFactoryService: ActionFactoryService,
- private actionService: ActionService, private router: Router, private libraryService: LibraryService) { }
+ private actionService: ActionService, private router: Router, private libraryService: LibraryService,
+ private seriesService: SeriesService) { }
ngOnInit(): void {
this.isChapter = this.utilityService.isChapter(this.data);
@@ -79,6 +83,10 @@ export class CardDetailsModalComponent implements OnInit {
this.chapters.forEach((c: Chapter) => {
c.files.sort((a: MangaFile, b: MangaFile) => collator.compare(a.filePath, b.filePath));
});
+
+ this.seriesService.getSeries(this.seriesId).subscribe(series => {
+ this.series = series;
+ })
}
close() {
diff --git a/UI/Web/src/app/collections/collection-detail/collection-detail.component.ts b/UI/Web/src/app/collections/collection-detail/collection-detail.component.ts
index 2593aaba8..2b4f6c4a4 100644
--- a/UI/Web/src/app/collections/collection-detail/collection-detail.component.ts
+++ b/UI/Web/src/app/collections/collection-detail/collection-detail.component.ts
@@ -11,6 +11,7 @@ import { EditCollectionTagsComponent } from 'src/app/cards/_modals/edit-collecti
import { KEY_CODES } from 'src/app/shared/_services/utility.service';
import { CollectionTag } from 'src/app/_models/collection-tag';
import { SeriesAddedToCollectionEvent } from 'src/app/_models/events/series-added-to-collection-event';
+import { SeriesRemovedEvent } from 'src/app/_models/events/series-removed-event';
import { Pagination } from 'src/app/_models/pagination';
import { Series } from 'src/app/_models/series';
import { FilterItem, mangaFormatFilters, SeriesFilter } from 'src/app/_models/series-filter';
@@ -106,8 +107,12 @@ export class CollectionDetailComponent implements OnInit, OnDestroy {
this.collectionTagActions = this.actionFactoryService.getCollectionTagActions(this.handleCollectionActionCallback.bind(this));
this.messageHub.messages$.pipe(takeWhile(event => event.event === EVENTS.SeriesAddedToCollection), takeUntil(this.onDestory), debounceTime(2000)).subscribe(event => {
- const collectionEvent = event.payload as SeriesAddedToCollectionEvent;
- if (collectionEvent.tagId === this.collectionTag.id) {
+ if (event.event == EVENTS.SeriesAddedToCollection) {
+ const collectionEvent = event.payload as SeriesAddedToCollectionEvent;
+ if (collectionEvent.tagId === this.collectionTag.id) {
+ this.loadPage();
+ }
+ } else if (event.event === EVENTS.SeriesRemoved) {
this.loadPage();
}
});
diff --git a/UI/Web/src/app/library/library.component.ts b/UI/Web/src/app/library/library.component.ts
index 94b1e61bc..fab0d4222 100644
--- a/UI/Web/src/app/library/library.component.ts
+++ b/UI/Web/src/app/library/library.component.ts
@@ -1,19 +1,15 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { Router } from '@angular/router';
-import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { Subject } from 'rxjs';
import { take, takeUntil } from 'rxjs/operators';
-import { EditCollectionTagsComponent } from '../cards/_modals/edit-collection-tags/edit-collection-tags.component';
-import { CollectionTag } from '../_models/collection-tag';
import { SeriesAddedEvent } from '../_models/events/series-added-event';
+import { SeriesRemovedEvent } from '../_models/events/series-removed-event';
import { InProgressChapter } from '../_models/in-progress-chapter';
import { Library } from '../_models/library';
import { Series } from '../_models/series';
import { User } from '../_models/user';
import { AccountService } from '../_services/account.service';
-import { Action, ActionFactoryService, ActionItem } from '../_services/action-factory.service';
-import { CollectionTagService } from '../_services/collection-tag.service';
import { ImageService } from '../_services/image.service';
import { LibraryService } from '../_services/library.service';
import { EVENTS, MessageHubService } from '../_services/message-hub.service';
@@ -44,14 +40,18 @@ export class LibraryComponent implements OnInit, OnDestroy {
private titleService: Title, public imageService: ImageService,
private messageHub: MessageHubService) {
this.messageHub.messages$.pipe(takeUntil(this.onDestroy)).subscribe(res => {
- if (res.event == EVENTS.SeriesAdded) {
+ if (res.event === EVENTS.SeriesAdded) {
const seriesAddedEvent = res.payload as SeriesAddedEvent;
this.seriesService.getSeries(seriesAddedEvent.seriesId).subscribe(series => {
this.recentlyAdded.unshift(series);
});
+ } else if (res.event === EVENTS.SeriesRemoved) {
+ const seriesRemovedEvent = res.payload as SeriesRemovedEvent;
+ this.recentlyAdded = this.recentlyAdded.filter(item => item.id != seriesRemovedEvent.seriesId);
+ this.inProgress = this.inProgress.filter(item => item.id != seriesRemovedEvent.seriesId);
}
});
- }
+ }
ngOnInit(): void {
this.titleService.setTitle('Kavita - Dashboard');
@@ -112,4 +112,11 @@ export class LibraryComponent implements OnInit, OnDestroy {
this.router.navigate(['in-progress']);
}
}
+
+ removeFromArray(arr: Array, element: any) {
+ const index = arr.indexOf(element);
+ if (index >= 0) {
+ arr.splice(index);
+ }
+ }
}
diff --git a/UI/Web/src/app/series-detail/series-detail.component.ts b/UI/Web/src/app/series-detail/series-detail.component.ts
index 65e99ebc0..356d99657 100644
--- a/UI/Web/src/app/series-detail/series-detail.component.ts
+++ b/UI/Web/src/app/series-detail/series-detail.component.ts
@@ -16,6 +16,7 @@ import { KEY_CODES, UtilityService } from '../shared/_services/utility.service';
import { ReviewSeriesModalComponent } from '../_modals/review-series-modal/review-series-modal.component';
import { Chapter } from '../_models/chapter';
import { ScanSeriesEvent } from '../_models/events/scan-series-event';
+import { SeriesRemovedEvent } from '../_models/events/series-removed-event';
import { LibraryType } from '../_models/library';
import { MangaFormat } from '../_models/manga-format';
import { Series } from '../_models/series';
@@ -26,7 +27,7 @@ import { ActionItem, ActionFactoryService, Action } from '../_services/action-fa
import { ActionService } from '../_services/action.service';
import { ImageService } from '../_services/image.service';
import { LibraryService } from '../_services/library.service';
-import { MessageHubService } from '../_services/message-hub.service';
+import { EVENTS, MessageHubService } from '../_services/message-hub.service';
import { ReaderService } from '../_services/reader.service';
import { SeriesService } from '../_services/series.service';
@@ -180,6 +181,16 @@ export class SeriesDetailComponent implements OnInit, OnDestroy {
this.toastr.success('Scan series completed');
});
+ this.messageHub.messages$.pipe(takeUntil(this.onDestroy)).subscribe(event => {
+ if (event.event === EVENTS.SeriesRemoved) {
+ const seriesRemovedEvent = event.payload as SeriesRemovedEvent;
+ if (seriesRemovedEvent.seriesId === this.series.id) {
+ this.toastr.info('This series no longer exists');
+ this.router.navigateByUrl('/libraries');
+ }
+ }
+ });
+
const seriesId = parseInt(routeId, 10);
this.libraryId = parseInt(libraryId, 10);
this.seriesImage = this.imageService.getSeriesCoverImage(seriesId);
diff --git a/UI/Web/src/app/shared/_services/utility.service.ts b/UI/Web/src/app/shared/_services/utility.service.ts
index 95751885e..18a7098cc 100644
--- a/UI/Web/src/app/shared/_services/utility.service.ts
+++ b/UI/Web/src/app/shared/_services/utility.service.ts
@@ -158,5 +158,4 @@ export class UtilityService {
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}
-
}