perf(discover): Improve the loading performance on the discover page

This commit is contained in:
tidusjar 2025-07-12 22:35:11 +01:00
commit 97d5167db6
7 changed files with 189 additions and 62 deletions

View file

@ -5,13 +5,17 @@
<mat-button-toggle id="{{id}}Tv" [ngClass]="{'button-active': discoverOptions === DiscoverOption.Tv}" value="{{DiscoverOption.Tv}}" class="discover-filter-button">{{'Discovery.Tv' | translate}}</mat-button-toggle> <mat-button-toggle id="{{id}}Tv" [ngClass]="{'button-active': discoverOptions === DiscoverOption.Tv}" value="{{DiscoverOption.Tv}}" class="discover-filter-button">{{'Discovery.Tv' | translate}}</mat-button-toggle>
</mat-button-toggle-group> </mat-button-toggle-group>
</div> </div>
@defer (when discoverResults.length > 0) { @defer (when discoverResults.length > 0; prefetch on idle) {
<p-carousel #carousel [numVisible]="10" [numScroll]="10" [page]="0" [value]="discoverResults" [responsiveOptions]="responsiveOptions" (onPage)="newPage()"> <p-carousel #carousel [numVisible]="10" [numScroll]="10" [page]="0" [value]="discoverResults" [responsiveOptions]="responsiveOptions" (onPage)="newPage()">
<ng-template let-result pTemplate="item"> <ng-template let-result pTemplate="item">
<discover-card [discoverType]="discoverType" [isAdmin]="isAdmin" [result]="result" [is4kEnabled]="is4kEnabled"></discover-card> <discover-card [discoverType]="discoverType" [isAdmin]="isAdmin" [result]="result" [is4kEnabled]="is4kEnabled"></discover-card>
</ng-template> </ng-template>
</p-carousel> </p-carousel>
} }
@placeholder(minimum 500) { @placeholder(minimum 300) {
<p-skeleton width="100%" height="18rem"></p-skeleton> <div class="row loading-container">
<div class="col-2" *ngFor="let item of [1,2,3,4,5,6,7,8,9,10]">
<p-skeleton width="100%" height="270px"></p-skeleton>
</div>
</div>
} }

View file

@ -105,6 +105,30 @@
padding: 5px; padding: 5px;
} }
.loading-container {
display: flex;
gap: 10px;
padding: 0 20px;
margin-top: 20px;
}
.loading-container .col-2 {
flex: 0 0 auto;
width: calc(10% - 9px);
}
@media (max-width: 768px) {
.loading-container .col-2 {
width: calc(50% - 5px);
}
}
@media (max-width: 480px) {
.loading-container .col-2 {
width: calc(100% - 0px);
}
}
@media (min-width:755px){ @media (min-width:755px){
::ng-deep .p-carousel-item{ ::ng-deep .p-carousel-item{
flex: 1 0 200px !important; flex: 1 0 200px !important;

View file

@ -43,7 +43,7 @@ export class CarouselListComponent implements OnInit {
get mediaTypeStorageKey() { get mediaTypeStorageKey() {
return "DiscoverOptions" + this.discoverType.toString(); return "DiscoverOptions" + this.discoverType.toString();
}; };
private amountToLoad = 17; private amountToLoad = 10;
private currentlyLoaded = 0; private currentlyLoaded = 0;
private baseUrl: string = ""; private baseUrl: string = "";
@ -148,6 +148,7 @@ export class CarouselListComponent implements OnInit {
} }
public async ngOnInit() { public async ngOnInit() {
this.is4kEnabled = this.featureFacade.is4kEnabled(); this.is4kEnabled = this.featureFacade.is4kEnabled();
this.currentlyLoaded = 0; this.currentlyLoaded = 0;
const localDiscoverOptions = +this.storageService.get(this.mediaTypeStorageKey); const localDiscoverOptions = +this.storageService.get(this.mediaTypeStorageKey);
@ -155,11 +156,15 @@ export class CarouselListComponent implements OnInit {
this.discoverOptions = DiscoverOption[DiscoverOption[localDiscoverOptions]]; this.discoverOptions = DiscoverOption[DiscoverOption[localDiscoverOptions]];
} }
let currentIteration = 0; // Load initial data - just enough to fill the first carousel page
while (this.discoverResults.length <= 14 && currentIteration <= 3) { // This reduces initial API calls and improves loading performance
currentIteration++; await this.loadData(false);
// If we don't have enough results to fill the carousel, load one more batch
if (this.discoverResults.length < 10) {
await this.loadData(false); await this.loadData(false);
} }
} }
public async toggleChanged(event: MatButtonToggleChange) { public async toggleChanged(event: MatButtonToggleChange) {

View file

@ -1,46 +1,108 @@
<div class="small-middle-container"> <div class="small-middle-container">
<div class="section"> @defer (on viewport; prefetch on idle) {
<h2 id="genreHeading" data-toggle="collapse" href="#genreCollapse" role="button">{{ 'Discovery.Genres' | translate }}</h2> <div class="section">
<genre-button-select class="collapse show" id="genreCollapse"></genre-button-select> <h2 id="genreHeading" data-toggle="collapse" href="#genreCollapse" role="button">{{ 'Discovery.Genres' | translate }}</h2>
</div> <genre-button-select class="collapse show" id="genreCollapse"></genre-button-select>
<div class="section">
<h2>{{ 'Discovery.RecentlyRequestedTab' | translate }}</h2>
<div>
<ombi-recently-list [id]="'recentlyRequested'"></ombi-recently-list>
</div> </div>
</div> } @placeholder(minimum 300) {
<div class="section">
<h2>{{ 'Discovery.Genres' | translate }}</h2>
<div class="section" [hidden]="!showSeasonal"> <p-skeleton width="100%" height="60px"></p-skeleton>
<h2>{{ 'Discovery.SeasonalTab' | translate }}</h2>
<div>
<carousel-list
[id]="'seasonal'"
[isAdmin]="isAdmin"
[discoverType]="DiscoverType.Seasonal"
(movieCount)="setSeasonalMovieCount($event)"
></carousel-list>
</div> </div>
</div> }
<div class="section"> @defer (on viewport; prefetch on idle) {
<h2>{{ 'Discovery.PopularTab' | translate }}</h2> <div class="section">
<div> <h2>{{ 'Discovery.RecentlyRequestedTab' | translate }}</h2>
<carousel-list [id]="'popular'" [isAdmin]="isAdmin" [discoverType]="DiscoverType.Popular"></carousel-list> <div>
<ombi-recently-list [id]="'recentlyRequested'"></ombi-recently-list>
</div>
</div> </div>
</div> } @placeholder(minimum 300) {
<div class="section">
<h2>{{ 'Discovery.RecentlyRequestedTab' | translate }}</h2>
<div class="row loading-container">
<div class="col-2" *ngFor="let item of [1,2,3,4,5]">
<p-skeleton width="100%" height="270px"></p-skeleton>
</div>
</div>
</div>
}
<div class="section"> @defer (on viewport; prefetch on idle) {
<h2>{{ 'Discovery.TrendingTab' | translate }}</h2> <div class="section" [hidden]="!showSeasonal">
<div> <h2>{{ 'Discovery.SeasonalTab' | translate }}</h2>
<carousel-list [id]="'trending'" [isAdmin]="isAdmin" [discoverType]="DiscoverType.Trending"></carousel-list> <div>
<carousel-list
[id]="'seasonal'"
[isAdmin]="isAdmin"
[discoverType]="DiscoverType.Seasonal"
(movieCount)="setSeasonalMovieCount($event)"
></carousel-list>
</div>
</div> </div>
</div> } @placeholder(minimum 300) {
<div class="section">
<h2>{{ 'Discovery.SeasonalTab' | translate }}</h2>
<div class="row loading-container">
<div class="col-2" *ngFor="let item of [1,2,3,4,5,6,7,8,9,10]">
<p-skeleton width="100%" height="270px"></p-skeleton>
</div>
</div>
</div>
}
<div class="section"> @defer (on viewport; prefetch on idle) {
<h2>{{ 'Discovery.UpcomingTab' | translate }}</h2> <div class="section">
<div> <h2>{{ 'Discovery.PopularTab' | translate }}</h2>
<carousel-list [id]="'upcoming'" [isAdmin]="isAdmin" [discoverType]="DiscoverType.Upcoming"></carousel-list> <div>
<carousel-list [id]="'popular'" [isAdmin]="isAdmin" [discoverType]="DiscoverType.Popular"></carousel-list>
</div>
</div> </div>
</div> } @placeholder(minimum 300) {
<div class="section">
<h2>{{ 'Discovery.PopularTab' | translate }}</h2>
<div class="row loading-container">
<div class="col-2" *ngFor="let item of [1,2,3,4,5,6,7,8,9,10]">
<p-skeleton width="100%" height="270px"></p-skeleton>
</div>
</div>
</div>
}
@defer (on viewport; prefetch on idle) {
<div class="section">
<h2>{{ 'Discovery.TrendingTab' | translate }}</h2>
<div>
<carousel-list [id]="'trending'" [isAdmin]="isAdmin" [discoverType]="DiscoverType.Trending"></carousel-list>
</div>
</div>
} @placeholder(minimum 300) {
<div class="section">
<h2>{{ 'Discovery.TrendingTab' | translate }}</h2>
<div class="row loading-container">
<div class="col-2" *ngFor="let item of [1,2,3,4,5,6,7,8,9,10]">
<p-skeleton width="100%" height="270px"></p-skeleton>
</div>
</div>
</div>
}
@defer (on viewport; prefetch on idle) {
<div class="section">
<h2>{{ 'Discovery.UpcomingTab' | translate }}</h2>
<div>
<carousel-list [id]="'upcoming'" [isAdmin]="isAdmin" [discoverType]="DiscoverType.Upcoming"></carousel-list>
</div>
</div>
} @placeholder(minimum 300) {
<div class="section">
<h2>{{ 'Discovery.UpcomingTab' | translate }}</h2>
<div class="row loading-container">
<div class="col-2" *ngFor="let item of [1,2,3,4,5,6,7,8,9,10]">
<p-skeleton width="100%" height="270px"></p-skeleton>
</div>
</div>
</div>
}
</div> </div>

View file

@ -10,3 +10,27 @@ h2{
margin-left:40px; margin-left:40px;
font-size: 24px; font-size: 24px;
} }
.loading-container {
display: flex;
gap: 10px;
padding: 0 20px;
margin-top: 20px;
}
.loading-container .col-2 {
flex: 0 0 auto;
width: calc(10% - 9px);
}
@media (max-width: 768px) {
.loading-container .col-2 {
width: calc(50% - 5px);
}
}
@media (max-width: 480px) {
.loading-container .col-2 {
width: calc(100% - 0px);
}
}

View file

@ -1,4 +1,4 @@
@defer (when requests()) { @defer (when requests(); prefetch on idle) {
<div *ngIf="requests().length > 0"> <div *ngIf="requests().length > 0">
<p-carousel #carousel [value]="requests()" [numVisible]="3" [numScroll]="1" <p-carousel #carousel [value]="requests()" [numVisible]="3" [numScroll]="1"
[responsiveOptions]="responsiveOptions" [page]="0"> [responsiveOptions]="responsiveOptions" [page]="0">
@ -13,21 +13,9 @@
</ng-template> </ng-template>
</p-carousel> </p-carousel>
</div> </div>
}@placeholder(minimum 500) { }@placeholder(minimum 300) {
<div class="row loading-container"> <div class="row loading-container">
<div class="col-2"> <div class="col-2" *ngFor="let item of [1,2,3,4,5]">
<p-skeleton width="100%" height="270px"></p-skeleton>
</div>
<div class="col-2">
<p-skeleton width="100%" height="270px"></p-skeleton>
</div>
<div class="col-2">
<p-skeleton width="100%" height="270px"></p-skeleton>
</div>
<div class="col-2">
<p-skeleton width="100%" height="270px"></p-skeleton>
</div>
<div class="col-2">
<p-skeleton width="100%" height="270px"></p-skeleton> <p-skeleton width="100%" height="270px"></p-skeleton>
</div> </div>
</div> </div>

View file

@ -105,12 +105,32 @@
padding: 5px; padding: 5px;
} }
.loading-container {
display: flex;
gap: 10px;
padding: 0 20px;
margin-top: 20px;
}
.loading-container .col-2 {
flex: 0 0 auto;
width: calc(20% - 8px);
}
@media (max-width: 768px) {
.loading-container .col-2 {
width: calc(50% - 5px);
}
}
@media (max-width: 480px) {
.loading-container .col-2 {
width: calc(100% - 0px);
}
}
@media (min-width:755px){ @media (min-width:755px){
::ng-deep .p-carousel-item{ ::ng-deep .p-carousel-item{
flex: 1 0 200px !important; flex: 1 0 200px !important;
} }
} }
.loading-container {
margin-left: 10rem;
}