Word Count (#1286)
* Adding some code for Robbie * See more on series detail metadata area is now at the bottom on the section * Cleaned up subtitle headings to use a single class for offset with actionables * Added some markup for the new design, waiting for Robbie to finish it off * styling age-rating badge * Started hooking up basic analyze file service and hooks in the UI. Basic code to implement the count is implemented and in benchmarks. * Hooked up analyze ui to backend * Refactored Series Detail metadata area to use a new icon/title design * Cleaned up the new design * Pushing for robbie to do css * Massive performance improvement to scan series where we only need to scan folders reported that have series in them, rather than the whole library. * Removed theme page as we no longer need it. Added WordCount to DTOs so the UI can show them. Added new pipe to format numbers in compact mode. * Hooked up actual reading time based on user's words per hour * Refactor some magic numbers to consts * Hooked in progress reporting for series word count * Hooked up analyze files * Re-implemented time to read on comics * Removed the word Last Read * Show proper language name instead of iso tag on series detail page. Added some error handling on word count code. * Reworked error handling * Fixed some security vulnerabilities in npm. * Handle a case where there are no text nodes and instead of returning an empty list, htmlagilitypack returns null. * Tweaked the styles a bit on the icon-and-title * Code cleanup Co-authored-by: Robbie Davis <robbie@therobbiedavis.com>
This commit is contained in:
parent
0a70ac35dc
commit
c1490d6e86
48 changed files with 2354 additions and 408 deletions
|
|
@ -9,20 +9,53 @@ import { Volume } from '../_models/volume';
|
|||
import { AccountService } from './account.service';
|
||||
|
||||
export enum Action {
|
||||
/**
|
||||
* Mark entity as read
|
||||
*/
|
||||
MarkAsRead = 0,
|
||||
/**
|
||||
* Mark entity as unread
|
||||
*/
|
||||
MarkAsUnread = 1,
|
||||
/**
|
||||
* Invoke a Scan Library
|
||||
*/
|
||||
ScanLibrary = 2,
|
||||
/**
|
||||
* Delete the entity
|
||||
*/
|
||||
Delete = 3,
|
||||
/**
|
||||
* Open edit modal
|
||||
*/
|
||||
Edit = 4,
|
||||
/**
|
||||
* Open details modal
|
||||
*/
|
||||
Info = 5,
|
||||
/**
|
||||
* Invoke a refresh covers
|
||||
*/
|
||||
RefreshMetadata = 6,
|
||||
/**
|
||||
* Download the entity
|
||||
*/
|
||||
Download = 7,
|
||||
/**
|
||||
* @deprecated This is no longer supported. Use the dedicated page instead
|
||||
* Invoke an Analyze Files which calculates word count
|
||||
*/
|
||||
AnalyzeFiles = 8,
|
||||
/**
|
||||
* Read in incognito mode aka no progress tracking
|
||||
*/
|
||||
Bookmarks = 8,
|
||||
IncognitoRead = 9,
|
||||
/**
|
||||
* Add to reading list
|
||||
*/
|
||||
AddToReadingList = 10,
|
||||
/**
|
||||
* Add to collection
|
||||
*/
|
||||
AddToCollection = 11,
|
||||
/**
|
||||
* Essentially a download, but handled differently. Needed so card bubbles it up for handling
|
||||
|
|
@ -31,7 +64,7 @@ export enum Action {
|
|||
/**
|
||||
* Open Series detail page for said series
|
||||
*/
|
||||
ViewSeries = 13
|
||||
ViewSeries = 13,
|
||||
}
|
||||
|
||||
export interface ActionItem<T> {
|
||||
|
|
@ -97,6 +130,13 @@ export class ActionFactoryService {
|
|||
requiresAdmin: true
|
||||
});
|
||||
|
||||
this.seriesActions.push({
|
||||
action: Action.AnalyzeFiles,
|
||||
title: 'Analyze Files',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: true
|
||||
});
|
||||
|
||||
this.seriesActions.push({
|
||||
action: Action.Delete,
|
||||
title: 'Delete',
|
||||
|
|
@ -131,6 +171,13 @@ export class ActionFactoryService {
|
|||
callback: this.dummyCallback,
|
||||
requiresAdmin: true
|
||||
});
|
||||
|
||||
this.libraryActions.push({
|
||||
action: Action.AnalyzeFiles,
|
||||
title: 'Analyze Files',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: true
|
||||
});
|
||||
|
||||
this.chapterActions.push({
|
||||
action: Action.Edit,
|
||||
|
|
@ -200,11 +247,6 @@ export class ActionFactoryService {
|
|||
return actions;
|
||||
}
|
||||
|
||||
filterBookmarksForFormat(action: ActionItem<Series>, series: Series) {
|
||||
if (action.action === Action.Bookmarks && series?.format === MangaFormat.EPUB) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
dummyCallback(action: Action, data: any) {}
|
||||
|
||||
_resetActions() {
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ export class ActionService implements OnDestroy {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Request a refresh of Metadata for a given Library
|
||||
* @param library Partial Library, must have id and name populated
|
||||
|
|
@ -90,6 +91,32 @@ export class ActionService implements OnDestroy {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Request an analysis of files for a given Library (currently just word count)
|
||||
* @param library Partial Library, must have id and name populated
|
||||
* @param callback Optional callback to perform actions after API completes
|
||||
* @returns
|
||||
*/
|
||||
async analyzeFiles(library: Partial<Library>, callback?: LibraryActionCallback) {
|
||||
if (!library.hasOwnProperty('id') || library.id === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!await this.confirmService.alert('This is a long running process. Please give it the time to complete before invoking again.')) {
|
||||
if (callback) {
|
||||
callback(library);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.libraryService.analyze(library?.id).pipe(take(1)).subscribe((res: any) => {
|
||||
this.toastr.info('Library file analysis queued for ' + library.name);
|
||||
if (callback) {
|
||||
callback(library);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a series as read; updates the series pagesRead
|
||||
* @param series Series, must have id and name populated
|
||||
|
|
@ -121,7 +148,7 @@ export class ActionService implements OnDestroy {
|
|||
}
|
||||
|
||||
/**
|
||||
* Start a file scan for a Series (currently just does the library not the series directly)
|
||||
* Start a file scan for a Series
|
||||
* @param series Series, must have libraryId and name populated
|
||||
* @param callback Optional callback to perform actions after API completes
|
||||
*/
|
||||
|
|
@ -134,6 +161,20 @@ export class ActionService implements OnDestroy {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a file scan for analyze files for a Series
|
||||
* @param series Series, must have libraryId and name populated
|
||||
* @param callback Optional callback to perform actions after API completes
|
||||
*/
|
||||
analyzeFilesForSeries(series: Series, callback?: SeriesActionCallback) {
|
||||
this.seriesService.analyzeFiles(series.libraryId, series.id).pipe(take(1)).subscribe((res: any) => {
|
||||
this.toastr.info('Scan queued for ' + series.name);
|
||||
if (callback) {
|
||||
callback(series);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a metadata refresh for a Series
|
||||
* @param series Series, must have libraryId, id and name populated
|
||||
|
|
|
|||
|
|
@ -74,6 +74,10 @@ export class LibraryService {
|
|||
return this.httpClient.post(this.baseUrl + 'library/scan?libraryId=' + libraryId, {});
|
||||
}
|
||||
|
||||
analyze(libraryId: number) {
|
||||
return this.httpClient.post(this.baseUrl + 'library/analyze?libraryId=' + libraryId, {});
|
||||
}
|
||||
|
||||
refreshMetadata(libraryId: number) {
|
||||
return this.httpClient.post(this.baseUrl + 'library/refresh-metadata?libraryId=' + libraryId, {});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ export class MetadataService {
|
|||
baseUrl = environment.apiUrl;
|
||||
|
||||
private ageRatingTypes: {[key: number]: string} | undefined = undefined;
|
||||
private validLanguages: Array<Language> = [];
|
||||
|
||||
constructor(private httpClient: HttpClient, private utilityService: UtilityService) { }
|
||||
|
||||
|
|
@ -81,7 +82,12 @@ export class MetadataService {
|
|||
* All the potential language tags there can be
|
||||
*/
|
||||
getAllValidLanguages() {
|
||||
return this.httpClient.get<Array<Language>>(this.baseUrl + 'metadata/all-languages');
|
||||
if (this.validLanguages != undefined && this.validLanguages.length > 0) {
|
||||
return of(this.validLanguages);
|
||||
}
|
||||
return this.httpClient.get<Array<Language>>(this.baseUrl + 'metadata/all-languages').pipe(map(l => this.validLanguages = l));
|
||||
|
||||
//return this.httpClient.get<Array<Language>>(this.baseUrl + 'metadata/all-languages').pipe();
|
||||
}
|
||||
|
||||
getAllPeople(libraries?: Array<number>) {
|
||||
|
|
|
|||
|
|
@ -145,6 +145,10 @@ export class SeriesService {
|
|||
return this.httpClient.post(this.baseUrl + 'series/scan', {libraryId: libraryId, seriesId: seriesId});
|
||||
}
|
||||
|
||||
analyzeFiles(libraryId: number, seriesId: number) {
|
||||
return this.httpClient.post(this.baseUrl + 'series/analyze', {libraryId: libraryId, seriesId: seriesId});
|
||||
}
|
||||
|
||||
getMetadata(seriesId: number) {
|
||||
return this.httpClient.get<SeriesMetadata>(this.baseUrl + 'series/metadata?seriesId=' + seriesId).pipe(map(items => {
|
||||
items?.collectionTags.forEach(tag => tag.coverImage = this.imageService.getCollectionCoverImage(tag.id));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue