Comic enhancements (#645)
* Adding multiple cases for comic naming conventions
* Changing "Chapter" to "Issue" for comic libraries
* Fixed an issue where the Parse method was using filename with extension to run regex matching, while it should be running on name without extension.
* Refactored to use Getter
* Cleaned up file to use conditional labelling rather than conditional html fragments
* Refactored code to properly check against library type for a given readinglist item
* Cleaned up series detail
* Conditionally remove special tags during parse
* Setup ParseInfoTests for ComicParserTests and also added unit tests from other comic issues created.
* Added more regex cases for naming patterns reported to be common with comics. Some cases added without regex.
* Pushing up changes
Fixed issue with cleanTitleTest.
Tried some patterns for "Cyberpunk 2077" but reverted
* Updated some cases and some spacing on Parser. Cyberpunk 2077 is not implemented as long as there is a # before issue number.
* Fixed the case for Special parsing on TPB. Fixed a piece of code that got deleted that prevented specials from rendering on volumes tab.
* Potential fix for parsing Cyberpunk 2077
- Added a ComicsSeriesSpecialCasesRegex and passed any filename that contains "Cyberpunk 2077" over to it so we can parse it separately. This could be used for any other potential problem series.
* Revert "Potential fix for parsing Cyberpunk 2077"
This reverts commit a14417e640
.
* Added more tests
* Refactored all places in Kavita to use Book, Issue, or Chapter depending on the Library type. Updated Volumes/Chapters to remove Volumes to make it cleaner.
* Removed some leftover test code
Co-authored-by: Joseph Milazzo <joseph.v.milazzo@gmail.com>
This commit is contained in:
parent
f5136c8127
commit
3293e5b424
10 changed files with 392 additions and 265 deletions
|
@ -22,6 +22,8 @@ import { MemberService } from 'src/app/_services/member.service';
|
|||
import { ReadingDirection } from 'src/app/_models/preferences/reading-direction';
|
||||
import { ScrollService } from 'src/app/scroll.service';
|
||||
import { MangaFormat } from 'src/app/_models/manga-format';
|
||||
import { LibraryService } from 'src/app/_services/library.service';
|
||||
import { LibraryType } from 'src/app/_models/library';
|
||||
|
||||
|
||||
interface PageStyle {
|
||||
|
@ -169,6 +171,10 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
* Last seen progress part path
|
||||
*/
|
||||
lastSeenScrollPartPath: string = '';
|
||||
/**
|
||||
* Library Type used for rendering chapter or issue
|
||||
*/
|
||||
libraryType: LibraryType = LibraryType.Book;
|
||||
|
||||
/**
|
||||
* Hack: Override background color for reader and restore it onDestroy
|
||||
|
@ -223,7 +229,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
private seriesService: SeriesService, private readerService: ReaderService, private location: Location,
|
||||
private renderer: Renderer2, private navService: NavService, private toastr: ToastrService,
|
||||
private domSanitizer: DomSanitizer, private bookService: BookService, private memberService: MemberService,
|
||||
private scrollService: ScrollService, private utilityService: UtilityService) {
|
||||
private scrollService: ScrollService, private utilityService: UtilityService, private libraryService: LibraryService) {
|
||||
this.navService.hideNavBar();
|
||||
|
||||
this.darkModeStyleElem = this.renderer.createElement('style');
|
||||
|
@ -421,6 +427,11 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
|
||||
|
||||
this.continuousChaptersStack.push(this.chapterId);
|
||||
|
||||
this.libraryService.getLibraryType(this.libraryId).pipe(take(1)).subscribe(type => {
|
||||
this.libraryType = type;
|
||||
});
|
||||
|
||||
|
||||
|
||||
if (this.pageNum >= this.maxPages) {
|
||||
|
@ -529,10 +540,10 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
const newRoute = this.readerService.getNextChapterUrl(this.router.url, this.chapterId, this.incognitoMode, this.readingListMode, this.readingListId);
|
||||
window.history.replaceState({}, '', newRoute);
|
||||
this.init();
|
||||
this.toastr.info(direction + ' chapter loaded', '', {timeOut: 3000});
|
||||
this.toastr.info(direction + ' ' + this.utilityService.formatChapterName(this.libraryType).toLowerCase() + ' loaded', '', {timeOut: 3000});
|
||||
} else {
|
||||
// This will only happen if no actual chapter can be found
|
||||
this.toastr.warning('Could not find ' + direction + ' chapter');
|
||||
this.toastr.warning('Could not find ' + direction.toLowerCase() + ' ' + this.utilityService.formatChapterName(this.libraryType).toLowerCase());
|
||||
this.isLoading = false;
|
||||
if (direction === 'Prev') {
|
||||
this.prevPageDisabled = true;
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
<div *ngIf="data !== undefined">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="modal-basic-title">
|
||||
<h4 *ngIf="libraryType !== LibraryType.Comic else comicHeader" class="modal-title" id="modal-basic-title">
|
||||
{{parentName}} - {{data.number != 0 ? (isChapter ? 'Chapter ' : 'Volume ') + data.number : 'Special'}} Details</h4>
|
||||
<ng-template #comicHeader><h4 class="modal-title" id="modal-basic-title">
|
||||
{{parentName}} - {{data.number != 0 ? (isChapter ? 'Issue #' : 'Volume ') + data.number : 'Special'}} Details</h4>
|
||||
</ng-template>
|
||||
<button type="button" class="close" aria-label="Close" (click)="close()">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
|
@ -27,18 +30,19 @@
|
|||
</div>
|
||||
</ng-container>
|
||||
|
||||
<h4 *ngIf="!utilityService.isChapter(data)">Chapters</h4>
|
||||
<h4 *ngIf="!utilityService.isChapter(data)">{{utilityService.formatChapterName(libraryType) + 's'}}</h4>
|
||||
<ul class="list-unstyled">
|
||||
<li class="media my-4" *ngFor="let chapter of chapters">
|
||||
<a (click)="readChapter(chapter)" href="javascript:void(0);" title="Read Chapter {{chapter.number}}">
|
||||
<a (click)="readChapter(chapter)" href="javascript:void(0);" title="Read {{libraryType !== LibraryType.Comic ? 'Chapter ' : 'Issue #'}} {{chapter.number}}">
|
||||
<img class="mr-3" style="width: 74px" [src]="chapter.coverImage">
|
||||
</a>
|
||||
<div class="media-body">
|
||||
<h5 class="mt-0 mb-1">
|
||||
<span *ngIf="chapter.number !== '0'; else specialHeader">
|
||||
<span class="">
|
||||
<app-card-actionables (actionHandler)="performAction($event, chapter)" [actions]="chapterActions" [labelBy]="'Chapter' + formatChapterNumber(chapter)"></app-card-actionables>
|
||||
</span>Chapter {{formatChapterNumber(chapter)}}
|
||||
<span >
|
||||
<app-card-actionables (actionHandler)="performAction($event, chapter)" [actions]="chapterActions" [labelBy]="utilityService.formatChapterName(libraryType, true, true) + formatChapterNumber(chapter)"></app-card-actionables>
|
||||
{{utilityService.formatChapterName(libraryType, true, false) }} {{formatChapterNumber(chapter)}}
|
||||
</span>
|
||||
<span class="badge badge-primary badge-pill">
|
||||
<span *ngIf="chapter.pagesRead > 0 && chapter.pagesRead < chapter.pages">{{chapter.pagesRead}} / {{chapter.pages}}</span>
|
||||
<span *ngIf="chapter.pagesRead === 0">UNREAD</span>
|
||||
|
|
|
@ -13,6 +13,8 @@ import { ActionService } from 'src/app/_services/action.service';
|
|||
import { ImageService } from 'src/app/_services/image.service';
|
||||
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';
|
||||
|
||||
|
||||
|
||||
|
@ -39,12 +41,16 @@ export class CardDetailsModalComponent implements OnInit {
|
|||
isAdmin: boolean = false;
|
||||
actions: ActionItem<any>[] = [];
|
||||
chapterActions: ActionItem<Chapter>[] = [];
|
||||
libraryType: LibraryType = LibraryType.Manga;
|
||||
|
||||
get LibraryType(): typeof LibraryType {
|
||||
return LibraryType;
|
||||
}
|
||||
|
||||
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 actionService: ActionService, private router: Router, private libraryService: LibraryService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.isChapter = this.utilityService.isChapter(this.data);
|
||||
|
@ -55,6 +61,10 @@ export class CardDetailsModalComponent implements OnInit {
|
|||
}
|
||||
});
|
||||
|
||||
this.libraryService.getLibraryType(this.libraryId).subscribe(type => {
|
||||
this.libraryType = type;
|
||||
});
|
||||
|
||||
this.chapterActions = this.actionFactoryService.getChapterActions(this.handleChapterActionCallback.bind(this)).filter(item => item.action !== Action.Edit);
|
||||
|
||||
if (this.isChapter) {
|
||||
|
@ -94,7 +104,7 @@ export class CardDetailsModalComponent implements OnInit {
|
|||
const chapter = this.utilityService.asChapter(this.data)
|
||||
chapter.coverImage = this.imageService.getChapterCoverImage(chapter.id);
|
||||
modalRef.componentInstance.chapter = chapter;
|
||||
modalRef.componentInstance.title = 'Select ' + (chapter.isSpecial ? '' : 'Chapter ') + chapter.range + '\'s Cover';
|
||||
modalRef.componentInstance.title = 'Select ' + (chapter.isSpecial ? '' : this.utilityService.formatChapterName(this.libraryType, false, true)) + chapter.range + '\'s Cover';
|
||||
} else {
|
||||
const volume = this.utilityService.asVolume(this.data);
|
||||
const chapters = volume.chapters;
|
||||
|
|
|
@ -12,7 +12,7 @@ import { ScalingOption } from '../_models/preferences/scaling-option';
|
|||
import { PageSplitOption } from '../_models/preferences/page-split-option';
|
||||
import { forkJoin, ReplaySubject, Subject } from 'rxjs';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { KEY_CODES } from '../shared/_services/utility.service';
|
||||
import { KEY_CODES, UtilityService } from '../shared/_services/utility.service';
|
||||
import { CircularArray } from '../shared/data-structures/circular-array';
|
||||
import { MemberService } from '../_services/member.service';
|
||||
import { Stack } from '../shared/data-structures/stack';
|
||||
|
@ -23,6 +23,8 @@ import { COLOR_FILTER, FITTING_OPTION, PAGING_DIRECTION, SPLIT_PAGE_PART } from
|
|||
import { Preferences, scalingOptions } from '../_models/preferences/preferences';
|
||||
import { READER_MODE } from '../_models/preferences/reader-mode';
|
||||
import { MangaFormat } from '../_models/manga-format';
|
||||
import { LibraryService } from '../_services/library.service';
|
||||
import { LibraryType } from '../_models/library';
|
||||
|
||||
const PREFETCH_PAGES = 5;
|
||||
|
||||
|
@ -205,6 +207,10 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
* Tracks if the first page is rendered or not. This is used to keep track of Automatic Scaling and adjusting decision after first page dimensions load up.
|
||||
*/
|
||||
firstPageRendered: boolean = false;
|
||||
/**
|
||||
* Library Type used for rendering chapter or issue
|
||||
*/
|
||||
libraryType: LibraryType = LibraryType.Manga;
|
||||
|
||||
private readonly onDestroy = new Subject<void>();
|
||||
|
||||
|
@ -260,7 +266,8 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
constructor(private route: ActivatedRoute, private router: Router, private accountService: AccountService,
|
||||
public readerService: ReaderService, private location: Location,
|
||||
private formBuilder: FormBuilder, private navService: NavService,
|
||||
private toastr: ToastrService, private memberService: MemberService) {
|
||||
private toastr: ToastrService, private memberService: MemberService,
|
||||
private libraryService: LibraryService, private utilityService: UtilityService) {
|
||||
this.navService.hideNavBar();
|
||||
}
|
||||
|
||||
|
@ -400,7 +407,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
forkJoin({
|
||||
progress: this.readerService.getProgress(this.chapterId),
|
||||
chapterInfo: this.readerService.getChapterInfo(this.chapterId),
|
||||
bookmarks: this.readerService.getBookmarks(this.chapterId)
|
||||
bookmarks: this.readerService.getBookmarks(this.chapterId),
|
||||
}).pipe(take(1)).subscribe(results => {
|
||||
|
||||
if (this.readingListMode && results.chapterInfo.seriesFormat === MangaFormat.EPUB) {
|
||||
|
@ -425,7 +432,12 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
newOptions.ceil = this.maxPages - 1; // We -1 so that the slider UI shows us hitting the end, since visually we +1 everything.
|
||||
this.pageOptions = newOptions;
|
||||
|
||||
this.updateTitle(results.chapterInfo);
|
||||
this.libraryService.getLibraryType(results.chapterInfo.libraryId).pipe(take(1)).subscribe(type => {
|
||||
this.libraryType = type;
|
||||
this.updateTitle(results.chapterInfo, type);
|
||||
});
|
||||
|
||||
|
||||
|
||||
// From bookmarks, create map of pages to make lookup time O(1)
|
||||
this.bookmarks = {};
|
||||
|
@ -479,7 +491,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
}
|
||||
}
|
||||
|
||||
updateTitle(chapterInfo: ChapterInfo) {
|
||||
updateTitle(chapterInfo: ChapterInfo, type: LibraryType) {
|
||||
this.title = chapterInfo.seriesName;
|
||||
if (chapterInfo.chapterTitle.length > 0) {
|
||||
this.title += ' - ' + chapterInfo.chapterTitle;
|
||||
|
@ -489,12 +501,12 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
if (chapterInfo.isSpecial && chapterInfo.volumeNumber === '0') {
|
||||
this.subtitle = chapterInfo.fileName;
|
||||
} else if (!chapterInfo.isSpecial && chapterInfo.volumeNumber === '0') {
|
||||
this.subtitle = 'Chapter ' + chapterInfo.chapterNumber;
|
||||
this.subtitle = this.utilityService.formatChapterName(type, true, true) + chapterInfo.chapterNumber;
|
||||
} else {
|
||||
this.subtitle = 'Volume ' + chapterInfo.volumeNumber;
|
||||
|
||||
if (chapterInfo.chapterNumber !== '0') {
|
||||
this.subtitle += ' Chapter ' + chapterInfo.chapterNumber;
|
||||
this.subtitle += ' ' + this.utilityService.formatChapterName(type, true, true) + chapterInfo.chapterNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -764,10 +776,10 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
const newRoute = this.readerService.getNextChapterUrl(this.router.url, this.chapterId, this.incognitoMode, this.readingListMode, this.readingListId);
|
||||
window.history.replaceState({}, '', newRoute);
|
||||
this.init();
|
||||
this.toastr.info(direction + ' chapter loaded', '', {timeOut: 3000});
|
||||
this.toastr.info(direction + ' ' + this.utilityService.formatChapterName(this.libraryType).toLowerCase() + ' loaded', '', {timeOut: 3000});
|
||||
} else {
|
||||
// This will only happen if no actual chapter can be found
|
||||
this.toastr.warning('Could not find ' + direction.toLowerCase() + ' chapter');
|
||||
this.toastr.warning('Could not find ' + direction.toLowerCase() + ' ' + this.utilityService.formatChapterName(this.libraryType).toLowerCase());
|
||||
this.isLoading = false;
|
||||
if (direction === 'Prev') {
|
||||
this.prevPageDisabled = true;
|
||||
|
|
|
@ -4,6 +4,7 @@ import { ToastrService } from 'ngx-toastr';
|
|||
import { take } from 'rxjs/operators';
|
||||
import { ConfirmService } from 'src/app/shared/confirm.service';
|
||||
import { UtilityService } from 'src/app/shared/_services/utility.service';
|
||||
import { LibraryType } from 'src/app/_models/library';
|
||||
import { MangaFormat } from 'src/app/_models/manga-format';
|
||||
import { ReadingList, ReadingListItem } from 'src/app/_models/reading-list';
|
||||
import { AccountService } from 'src/app/_services/account.service';
|
||||
|
@ -12,6 +13,8 @@ import { ActionService } from 'src/app/_services/action.service';
|
|||
import { ImageService } from 'src/app/_services/image.service';
|
||||
import { ReadingListService } from 'src/app/_services/reading-list.service';
|
||||
import { IndexUpdateEvent, ItemRemoveEvent } from '../dragable-ordered-list/dragable-ordered-list.component';
|
||||
import { LibraryService } from '../../_services/library.service';
|
||||
import { forkJoin } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'app-reading-list-detail',
|
||||
|
@ -19,7 +22,6 @@ import { IndexUpdateEvent, ItemRemoveEvent } from '../dragable-ordered-list/drag
|
|||
styleUrls: ['./reading-list-detail.component.scss']
|
||||
})
|
||||
export class ReadingListDetailComponent implements OnInit {
|
||||
|
||||
items: Array<ReadingListItem> = [];
|
||||
listId!: number;
|
||||
readingList!: ReadingList;
|
||||
|
@ -32,6 +34,7 @@ export class ReadingListDetailComponent implements OnInit {
|
|||
hasDownloadingRole: boolean = false;
|
||||
downloadInProgress: boolean = false;
|
||||
|
||||
libraryTypes: {[key: number]: LibraryType} = {};
|
||||
|
||||
get MangaFormat(): typeof MangaFormat {
|
||||
return MangaFormat;
|
||||
|
@ -39,7 +42,8 @@ export class ReadingListDetailComponent implements OnInit {
|
|||
|
||||
constructor(private route: ActivatedRoute, private router: Router, private readingListService: ReadingListService,
|
||||
private actionService: ActionService, private actionFactoryService: ActionFactoryService, public utilityService: UtilityService,
|
||||
public imageService: ImageService, private accountService: AccountService, private toastr: ToastrService, private confirmService: ConfirmService) {}
|
||||
public imageService: ImageService, private accountService: AccountService, private toastr: ToastrService,
|
||||
private confirmService: ConfirmService, private libraryService: LibraryService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
const listId = this.route.snapshot.paramMap.get('id');
|
||||
|
@ -51,7 +55,21 @@ export class ReadingListDetailComponent implements OnInit {
|
|||
|
||||
this.listId = parseInt(listId, 10);
|
||||
|
||||
this.readingListService.getReadingList(this.listId).subscribe(readingList => {
|
||||
this.libraryService.getLibraries().subscribe(libs => {
|
||||
|
||||
});
|
||||
|
||||
forkJoin([
|
||||
this.libraryService.getLibraries(),
|
||||
this.readingListService.getReadingList(this.listId)
|
||||
]).subscribe(results => {
|
||||
const libraries = results[0];
|
||||
const readingList = results[1];
|
||||
|
||||
libraries.forEach(lib => {
|
||||
this.libraryTypes[lib.id] = lib.type;
|
||||
});
|
||||
|
||||
if (readingList == null) {
|
||||
// The list doesn't exist
|
||||
this.toastr.error('This list doesn\'t exist.');
|
||||
|
@ -81,7 +99,6 @@ export class ReadingListDetailComponent implements OnInit {
|
|||
}
|
||||
|
||||
performAction(action: ActionItem<any>) {
|
||||
// TODO: Try to move performAction into the actionables component. (have default handler in the component, allow for overridding to pass additional context)
|
||||
if (typeof action.callback === 'function') {
|
||||
action.callback(action.action, this.readingList);
|
||||
}
|
||||
|
@ -119,7 +136,7 @@ export class ReadingListDetailComponent implements OnInit {
|
|||
return 'Volume ' + this.utilityService.cleanSpecialTitle(item.chapterNumber);
|
||||
}
|
||||
|
||||
return 'Chapter ' + item.chapterNumber;
|
||||
return this.utilityService.formatChapterName(this.libraryTypes[item.libraryId], true, true) + item.chapterNumber;
|
||||
}
|
||||
|
||||
orderUpdated(event: IndexUpdateEvent) {
|
||||
|
|
|
@ -112,7 +112,7 @@
|
|||
</ng-template>
|
||||
</li>
|
||||
<li [ngbNavItem]="2" *ngIf="hasNonSpecialVolumeChapters">
|
||||
<a ngbNavLink>Volumes/Chapters</a>
|
||||
<a ngbNavLink>{{utilityService.formatChapterName(libraryType) + 's'}}</a>
|
||||
<ng-template ngbNavContent>
|
||||
<div class="row no-gutters">
|
||||
<div *ngFor="let volume of volumes; let idx = index; trackBy: trackByVolumeIdentity">
|
||||
|
@ -121,7 +121,7 @@
|
|||
[read]="volume.pagesRead" [total]="volume.pages" [actions]="volumeActions" (selection)="bulkSelectionService.handleCardSelection('volume', idx, volumes.length, $event)" [selected]="bulkSelectionService.isCardSelected('volume', idx)" [allowSelection]="true"></app-card-item>
|
||||
</div>
|
||||
<div *ngFor="let chapter of chapters; let idx = index; trackBy: trackByChapterIdentity">
|
||||
<app-card-item class="col-auto" *ngIf="!chapter.isSpecial" [entity]="chapter" [title]="'Chapter ' + chapter.range" (click)="openChapter(chapter)"
|
||||
<app-card-item class="col-auto" *ngIf="!chapter.isSpecial" [entity]="chapter" [title]="utilityService.formatChapterName(libraryType, true, true) + chapter.range" (click)="openChapter(chapter)"
|
||||
[imageUrl]="imageService.getChapterCoverImage(chapter.id) + '&offset=' + coverImageOffset"
|
||||
[read]="chapter.pagesRead" [total]="chapter.pages" [actions]="chapterActions" (selection)="bulkSelectionService.handleCardSelection('chapter', idx, chapters.length, $event)" [selected]="bulkSelectionService.isCardSelected('chapter', idx)" [allowSelection]="true"></app-card-item>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { Chapter } from 'src/app/_models/chapter';
|
||||
import { LibraryType } from 'src/app/_models/library';
|
||||
import { MangaFormat } from 'src/app/_models/manga-format';
|
||||
import { Series } from 'src/app/_models/series';
|
||||
import { Volume } from 'src/app/_models/volume';
|
||||
|
@ -56,6 +57,27 @@ export class UtilityService {
|
|||
return this.mangaFormatKeys.filter(item => MangaFormat[format] === item)[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a Chapter name based on the library it's in
|
||||
* @param libraryType
|
||||
* @param includeHash For comics only, includes a # which is used for numbering on cards
|
||||
* @param includeSpace Add a space at the end of the string. if includeHash and includeSpace are true, only hash will be at the end.
|
||||
* @returns
|
||||
*/
|
||||
formatChapterName(libraryType: LibraryType, includeHash: boolean = false, includeSpace: boolean = false) {
|
||||
switch(libraryType) {
|
||||
case LibraryType.Book:
|
||||
return 'Book' + (includeSpace ? ' ' : '');
|
||||
case LibraryType.Comic:
|
||||
if (includeHash) {
|
||||
return 'Issue #';
|
||||
}
|
||||
return 'Issue' + (includeSpace ? ' ' : '');
|
||||
case LibraryType.Manga:
|
||||
return 'Chapter' + (includeSpace ? ' ' : '');
|
||||
}
|
||||
}
|
||||
|
||||
cleanSpecialTitle(title: string) {
|
||||
let cleaned = title.replace(/_/g, ' ').replace(/SP\d+/g, '').trim();
|
||||
cleaned = cleaned.substring(0, cleaned.lastIndexOf('.'));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue