Implemented the ability to allow the admin to change the cover generation size. (#2213)

Changed how covers are merged together. Now a cover image will always be generated for reading list and collections.

Fixed reading list page being a bit laggy due to a missing trackby function.

Reading list page will now show the cover image always. Collection detail page will only hide the image if there is no summary on the collection.
This commit is contained in:
Joe Milazzo 2023-08-14 06:56:09 -05:00 committed by GitHub
parent 19801af6f3
commit d134196470
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 221 additions and 87 deletions

View file

@ -0,0 +1,14 @@
export enum CoverImageSize {
Default = 1,
Medium = 2,
Large = 3,
XLarge = 4
}
export const CoverImageSizes =
[
{value: CoverImageSize.Default, title: 'cover-image-size.default'},
{value: CoverImageSize.Medium, title: 'cover-image-size.medium'},
{value: CoverImageSize.Large, title: 'cover-image-size.large'},
{value: CoverImageSize.XLarge, title: 'cover-image-size.xlarge'}
];

View file

@ -1,4 +1,5 @@
import { EncodeFormat } from "./encode-format";
import {CoverImageSize} from "./cover-image-size";
export interface ServerSettings {
cacheDirectory: string;
@ -20,4 +21,5 @@ export interface ServerSettings {
cacheSize: number;
onDeckProgressDays: number;
onDeckUpdateDays: number;
coverImageSize: CoverImageSize;
}

View file

@ -7,7 +7,7 @@
<br/><b>{{t('encode-as-warning')}}</b>
</p>
<div *ngIf="settingsForm.get('encodeMediaAs')?.dirty" class="alert alert-danger" role="alert">{{t('media-warning')}}</div>
<div class="col-md-6 col-sm-12 mb-3">
<div class="col-md-6 col-sm-12 mb-3 pe-1">
<label for="settings-media-encodeMediaAs" class="form-label me-1">{{t('encode-as-label')}}</label>
<i class="fa fa-info-circle" placement="right" [ngbTooltip]="encodeMediaAsTooltip" role="button" tabindex="0"></i>
<ng-template #encodeMediaAsTooltip>{{t('encode-as-tooltip')}}</ng-template>
@ -16,6 +16,16 @@
<option *ngFor="let format of EncodeFormats" [value]="format.value">{{format.title}}</option>
</select>
</div>
<div class="col-md-6 col-sm-12 mb-3">
<label for="settings-media-coverImageSize" class="form-label me-1">{{t('cover-image-size-label')}}</label>
<i class="fa fa-info-circle" placement="right" [ngbTooltip]="coverImageSizeTooltip" role="button" tabindex="0"></i>
<ng-template #coverImageSizeTooltip>{{t('cover-image-size-tooltip')}}</ng-template>
<span class="visually-hidden" id="settings-media-coverImageSize-help"><ng-container [ngTemplateOutlet]="coverImageSizeTooltip"></ng-container></span>
<select class="form-select" aria-describedby="settings-media-coverImageSize-help" formControlName="coverImageSize" id="settings-media-coverImageSize">
<option *ngFor="let size of coverImageSizes" [value]="size.value">{{size.title}}</option>
</select>
</div>
</div>
<div class="row g-0">

View file

@ -21,7 +21,8 @@ import {EncodeFormats} from '../_models/encode-format';
import {ManageScrobbleErrorsComponent} from '../manage-scrobble-errors/manage-scrobble-errors.component';
import {ManageAlertsComponent} from '../manage-alerts/manage-alerts.component';
import {NgFor, NgIf, NgTemplateOutlet} from '@angular/common';
import {TranslocoDirective, TranslocoService} from "@ngneat/transloco";
import {translate, TranslocoDirective, TranslocoService} from "@ngneat/transloco";
import { CoverImageSizes } from '../_models/cover-image-size';
@Component({
selector: 'app-manage-media-settings',
@ -38,6 +39,11 @@ export class ManageMediaSettingsComponent implements OnInit {
alertCount: number = 0;
scrobbleCount: number = 0;
coverImageSizes = CoverImageSizes.map(o => {
const newObj = {...o};
newObj.title = translate(o.title);
return newObj;
})
private readonly translocoService = inject(TranslocoService);
private readonly cdRef = inject(ChangeDetectorRef);
@ -51,6 +57,7 @@ export class ManageMediaSettingsComponent implements OnInit {
this.serverSettings = settings;
this.settingsForm.addControl('encodeMediaAs', new FormControl(this.serverSettings.encodeMediaAs, [Validators.required]));
this.settingsForm.addControl('bookmarksDirectory', new FormControl(this.serverSettings.bookmarksDirectory, [Validators.required]));
this.settingsForm.addControl('coverImageSize', new FormControl(this.serverSettings.coverImageSize, [Validators.required]));
this.cdRef.markForCheck();
});
}
@ -58,6 +65,7 @@ export class ManageMediaSettingsComponent implements OnInit {
resetForm() {
this.settingsForm.get('encodeMediaAs')?.setValue(this.serverSettings.encodeMediaAs);
this.settingsForm.get('bookmarksDirectory')?.setValue(this.serverSettings.bookmarksDirectory);
this.settingsForm.get('coverImageSize')?.setValue(this.serverSettings.coverImageSize);
this.settingsForm.markAsPristine();
this.cdRef.markForCheck();
}
@ -66,6 +74,7 @@ export class ManageMediaSettingsComponent implements OnInit {
const modelSettings = Object.assign({}, this.serverSettings);
modelSettings.encodeMediaAs = parseInt(this.settingsForm.get('encodeMediaAs')?.value, 10);
modelSettings.bookmarksDirectory = this.settingsForm.get('bookmarksDirectory')?.value;
modelSettings.coverImageSize = parseInt(this.settingsForm.get('coverImageSize')?.value, 10);
this.settingsService.updateServerSettings(modelSettings).pipe(take(1)).subscribe(async (settings: ServerSettings) => {
this.serverSettings = settings;

View file

@ -11,7 +11,9 @@
[trackByIdentity]="trackByIdentity"
>
<ng-template #cardItem let-item let-position="idx">
<app-card-item [title]="item.title" [entity]="item" [actions]="collectionTagActions" [imageUrl]="imageSerivce.getCollectionCoverImage(item.id)" (clicked)="loadCollection(item)"></app-card-item>
<app-card-item [title]="item.title" [entity]="item" [actions]="collectionTagActions"
[imageUrl]="imageSerivce.getCollectionCoverImage(item.id)"
(clicked)="loadCollection(item)"></app-card-item>
</ng-template>
<ng-template #noData>

View file

@ -11,9 +11,9 @@
</div>
<div [ngStyle]="{'height': ScrollingBlockHeight}" class="main-container container-fluid pt-2" *ngIf="collectionTag !== undefined" #scrollingBlock>
<div class="row mb-3" *ngIf="(collectionTag.coverImage !== '' && collectionTag.coverImage !== undefined && collectionTag.coverImage !== null) || summary.length > 0">
<div class="col-md-2 col-xs-4 col-sm-6 d-none d-sm-block" *ngIf="collectionTag.coverImage !== '' && collectionTag.coverImage !== undefined && collectionTag.coverImage !== null">
<app-image maxWidth="481px" [imageUrl]="tagImage"></app-image>
<div class="row mb-3" *ngIf="summary.length > 0">
<div class="col-md-2 col-xs-4 col-sm-6 d-none d-sm-block">
<app-image maxWidth="481px" [imageUrl]="imageService.getCollectionCoverImage(collectionTag.id)"></app-image>
</div>
<div class="col-md-10 col-xs-8 col-sm-6 mt-2">
<app-read-more [text]="summary" [maxLength]="250"></app-read-more>

View file

@ -70,7 +70,6 @@ export class CollectionDetailComponent implements OnInit, AfterContentChecked {
translocoService = inject(TranslocoService);
collectionTag!: CollectionTag;
tagImage: string = '';
isLoading: boolean = true;
series: Array<Series> = [];
pagination!: Pagination;
@ -229,9 +228,6 @@ export class CollectionDetailComponent implements OnInit, AfterContentChecked {
this.collectionTag = matchingTags[0];
this.summary = (this.collectionTag.summary === null ? '' : this.collectionTag.summary).replace(/\n/g, '<br>');
// TODO: This can be changed now that we have app-image and collection cover merge code (can it? Because we still have the case where there is no image)
// I can always tweak merge to allow blank slots and if just one item, just show that item image
this.tagImage = this.imageService.randomize(this.imageService.getCollectionCoverImage(this.collectionTag.id));
this.titleService.setTitle(this.translocoService.translate('collection-detail.title-alt', {collectionName: this.collectionTag.title}));
this.cdRef.markForCheck();
});
@ -285,11 +281,6 @@ export class CollectionDetailComponent implements OnInit, AfterContentChecked {
modalRef.closed.subscribe((results: {success: boolean, coverImageUpdated: boolean}) => {
this.updateTag(this.collectionTag.id);
this.loadPage();
if (results.coverImageUpdated) {
this.tagImage = this.imageService.randomize(this.imageService.getCollectionCoverImage(collectionTag.id));
this.collectionTag.coverImage = this.imageService.randomize(this.imageService.getCollectionCoverImage(collectionTag.id));
this.cdRef.markForCheck();
}
});
}

View file

@ -38,8 +38,8 @@
<div class="container-fluid mt-2" *ngIf="readingList" >
<div class="row mb-2">
<div class="col-md-2 col-xs-4 col-sm-6 d-none d-sm-block" *ngIf="readingList.coverImage !== '' && readingList.coverImage !== undefined && readingList.coverImage !== null">
<app-image maxWidth="300px" maxHeight="400px" [imageUrl]="readingListImage"></app-image>
<div class="col-md-2 col-xs-4 col-sm-6 d-none d-sm-block">
<app-image maxWidth="300px" maxHeight="400px" [imageUrl]="imageService.getReadingListCoverImage(readingList.id)"></app-image>
</div>
<div class="col-md-10 col-xs-8 col-sm-6 mt-2">
<div class="row g-0 mb-3">

View file

@ -62,7 +62,6 @@ export class ReadingListDetailComponent implements OnInit {
downloadInProgress: boolean = false;
readingListSummary: string = '';
readingListImage: string = '';
libraryTypes: {[key: number]: LibraryType} = {};
characters$!: Observable<Person[]>;
@ -90,7 +89,6 @@ export class ReadingListDetailComponent implements OnInit {
}
this.listId = parseInt(listId, 10);
this.characters$ = this.readingListService.getCharacters(this.listId);
this.readingListImage = this.imageService.randomize(this.imageService.getReadingListCoverImage(this.listId));
forkJoin([
this.libraryService.getLibraries(),
@ -162,7 +160,6 @@ export class ReadingListDetailComponent implements OnInit {
this.readingListService.getReadingList(this.listId).subscribe(rl => {
this.readingList = rl;
this.readingListSummary = (this.readingList.summary === null ? '' : this.readingList.summary).replace(/\n/g, '<br>');
this.readingListImage = this.imageService.randomize(this.imageService.getReadingListCoverImage(this.listId));
this.cdRef.markForCheck();
})
});

View file

@ -2,7 +2,7 @@
<app-side-nav-companion-bar>
<h2 title>
<app-card-actionables [actions]="globalActions" (actionHandler)="performGlobalAction($event)"></app-card-actionables>
<span>Reading Lists</span>
<span>{{t('title')}}</span>
</h2>
<h6 subtitle class="subtitle-with-actionables" *ngIf="pagination">{{t('item-count', {num: pagination.totalItems | number})}}</h6>
</app-side-nav-companion-bar>
@ -13,6 +13,7 @@
[pagination]="pagination"
[jumpBarKeys]="jumpbarKeys"
[filteringDisabled]="true"
[trackByIdentity]="trackByIdentity"
>
<ng-template #cardItem let-item let-position="idx" >
<app-card-item [title]="item.title" [entity]="item" [actions]="actions[item.id]"

View file

@ -19,6 +19,7 @@ import { NgIf, DecimalPipe } from '@angular/common';
import { SideNavCompanionBarComponent } from '../../../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component';
import {TranslocoDirective, TranslocoService} from "@ngneat/transloco";
import {CardActionablesComponent} from "../../../_single-module/card-actionables/card-actionables.component";
import {CollectionTag} from "../../../_models/collection-tag";
@Component({
selector: 'app-reading-lists',
@ -37,6 +38,7 @@ export class ReadingListsComponent implements OnInit {
jumpbarKeys: Array<JumpKey> = [];
actions: {[key: number]: Array<ActionItem<ReadingList>>} = {};
globalActions: Array<ActionItem<any>> = [{action: Action.Import, title: 'import-cbl', children: [], requiresAdmin: true, callback: this.importCbl.bind(this)}];
trackByIdentity = (index: number, item: ReadingList) => `${item.id}_${item.title}`;
translocoService = inject(TranslocoService);
constructor(private readingListService: ReadingListService, public imageService: ImageService, private actionFactoryService: ActionFactoryService,

View file

@ -1069,7 +1069,16 @@
"save": "{{common.save}}",
"media-issue-title": "Media Issues",
"scrobble-issue-title": "Scrobble Issues"
"scrobble-issue-title": "Scrobble Issues",
"cover-image-size-label": "Cover Image Size",
"cover-image-size-tooltip": "How large should cover images generate as. Note: Anything larger than the default will incur longer page load times."
},
"cover-image-size": {
"default": "Default (320x455)",
"medium": "Medium (640x909)",
"large": "Large (900x1277)",
"xlarge": "Extra Large (1265x1795)"
},
"manage-scrobble-errors": {