Skip to content

Commit

Permalink
Web App: Performance improvement for Videos tab
Browse files Browse the repository at this point in the history
Usa a lazy load with virtual scroll for the Videos tab.

The directories will now all be listed first, before files if there are
also files in the same location.
  • Loading branch information
bennettpeter committed Sep 9, 2023
1 parent b0248db commit 1f06b21
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
<p-toast position="center"></p-toast>
</div>
<h2>{{ 'dashboard.videos.heading' | translate }}</h2>
<div class="block card w-full" style="height: 90vh" *ngIf="loaded else loading">
<div class="block card w-full" style="height: 90vh">
<p-menu #menu [popup]="true" [model]="menuToShow"></p-menu>
<p-table [value]="videos" scrollHeight="flex" [scrollable]="true" styleClass="p-datatable-striped"
[rowHover]="true" responsiveLayout="scroll">
<p-table #table name="table" [value]="videos" scrollHeight="flex" [scrollable]="true" styleClass="p-datatable-striped"
[virtualScroll]="true" [rows]="50" [lazy]="true" (onLazyLoad)="loadLazy($event)" [rowHover]="true"
responsiveLayout="scroll">
<ng-template pTemplate="caption">
</ng-template>
<ng-template pTemplate="header">
Expand All @@ -31,7 +32,7 @@ <h2>{{ 'dashboard.videos.heading' | translate }}</h2>
[style]="{width: '30px', height: '30px'}"></p-progressSpinner></div>
<ng-template #refreshBn>
<button pButton pRipple icon="pi pi-refresh" class="p-button-text .p-button-success"
(click)="refreshing=true;loadVideos()"
(click)="refreshing=true;reLoadVideos()"
pTooltip="{{ 'common.refresh' | translate }}"></button>
</ng-template>
&nbsp;&nbsp;&nbsp;{{ videos.length }} Rows
Expand Down Expand Up @@ -62,7 +63,7 @@ <h2>{{ 'dashboard.videos.heading' | translate }}</h2>
<ng-template pTemplate="body" let-video>
<tr>
<td style="flex-basis: 30%" class="p-1 overflow-hidden">
<div *ngIf="video.ContentType == 'D' else title">
<div *ngIf="video.ContentType == 'DIRECTORY' else title">
<button pButton pRipple icon="pi pi-folder" class="p-button-text p-button-primary"
(click)="onDirectory(video.Title)" label="{{video.Title}}"></button>
</div>
Expand All @@ -86,7 +87,7 @@ <h2>{{ 'dashboard.videos.heading' | translate }}</h2>
<div *ngIf="video.Length > 0"> {{ video.Length }} min </div>
</td>
<td style="flex-basis: 6%" class="p-1">
<div *ngIf="video.ContentType != 'D'">
<div *ngIf="video.ContentType != 'DIRECTORY'">
<a href="{{URLencode('/Content/GetVideo?Id=' + video.Id)}}" target="_blank"
pTooltip="{{ 'dashboard.videos.download' | translate }}" tooltipPosition="left">
<i class="pi pi-download text-primary"></i></a>
Expand Down Expand Up @@ -178,7 +179,4 @@ <h2>{{ 'dashboard.videos.heading' | translate }}</h2>
</p-dialog>

</form>
<ng-template #loading>
<p-progressSpinner></p-progressSpinner>
</ng-template>
</div>
104 changes: 49 additions & 55 deletions mythtv/html/backend/src/app/dashboard/videos/videos.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import { NgForm } from '@angular/forms';
import { TranslateService } from '@ngx-translate/core';
import { LazyLoadEvent, MenuItem, MessageService } from 'primeng/api';
import { Menu } from 'primeng/menu';
import { Table } from 'primeng/table';
import { PartialObserver } from 'rxjs';
import { UpdateVideoMetadataRequest, VideoMetadataInfo } from 'src/app/services/interfaces/video.interface';
import { GetVideoListRequest, UpdateVideoMetadataRequest, VideoMetadataInfo } from 'src/app/services/interfaces/video.interface';
import { UtilityService } from 'src/app/services/utility.service';
import { VideoService } from 'src/app/services/video.service';

Expand All @@ -18,11 +19,10 @@ export class VideosComponent implements OnInit {

@ViewChild("vidsform") currentForm!: NgForm;
@ViewChild("menu") menu!: Menu;
@ViewChild("table") table!: Table;

allVideos: VideoMetadataInfo[] = [];
videos: VideoMetadataInfo[] = [];
refreshing = false;
loaded = false;
successCount = 0;
errorCount = 0;
directory: string[] = [];
Expand All @@ -31,6 +31,7 @@ export class VideosComponent implements OnInit {
displayMetadataDlg = false;
displayUnsaved = false;
showAllVideos = false;
lazyLoadEvent!: LazyLoadEvent;

mnu_markwatched: MenuItem = { label: 'dashboard.recordings.mnu_markwatched', command: (event) => this.markwatched(event, true) };
mnu_markunwatched: MenuItem = { label: 'dashboard.recordings.mnu_markunwatched', command: (event) => this.markwatched(event, false) };
Expand All @@ -47,8 +48,6 @@ export class VideosComponent implements OnInit {

constructor(private videoService: VideoService, private translate: TranslateService,
private messageService: MessageService, public utility: UtilityService) {
this.loadVideos();

// translations
for (const [key, value] of Object.entries(this.msg)) {
this.translate.get(value).subscribe(data => {
Expand All @@ -69,61 +68,56 @@ export class VideosComponent implements OnInit {
ngOnInit(): void {
}

loadLazy(event: LazyLoadEvent) {
this.lazyLoadEvent = event;
let request: GetVideoListRequest = {
Sort: "Title",
Folder: this.directory.join('/'),
CollapseSubDirs: !this.showAllVideos,
StartIndex: 0,
Count: 1
};

if (event.sortField) {
// if (event.sortField == 'Title')
// request.Sort = 'FileName';
// else
request.Sort = event.sortField;
if (event.sortOrder)
request.Descending = (event.sortOrder < 0);
}

if (event.first)
request.StartIndex = event.first;
if (event.rows) {
request.Count = event.rows;
}

loadVideos() {
this.videoService.GetVideoList({ Sort: "FileName" }).subscribe(data => {
this.allVideos = data.VideoMetadataInfoList.VideoMetadataInfos;
this.filterVideos();
this.loaded = true;
this.videoService.GetVideoList(request).subscribe(data => {
let newList = data.VideoMetadataInfoList;
this.videos.length = data.VideoMetadataInfoList.TotalAvailable;
// populate page of virtual programs
this.videos.splice(newList.StartIndex, newList.Count,
...newList.VideoMetadataInfos);
// notify of change
this.videos = [...this.videos]
this.refreshing = false;
});

}

showAllChange() {
this.refreshing = true;
setTimeout(() => this.filterVideos(), 100);

reLoadVideos() {
this.table.resetScrollTop();
this.videos.length = 0;
this.lazyLoadEvent.first = 0;
this.lazyLoadEvent.rows = 100;
this.loadLazy(this.lazyLoadEvent);
}

filterVideos() {
if (this.showAllVideos) {
this.directory.length = 0;
this.videos = [...this.allVideos];
}
else {
this.videos = [];
let prior = '';
let search = this.directory.join('/');
if (search.length > 0)
search += '/';
this.allVideos.forEach(
video => {
const parts = video.FileName.split('/');
if (video.FileName.startsWith(search)) {
if (parts.length == this.directory.length + 1) {
this.videos.push(video);
prior = '';
}
else {
let dir = parts.slice(0, this.directory.length + 1)
.join('/') + '/';
if (dir != prior) {
// Dummy directory entry
this.videos.push(<VideoMetadataInfo>{
FileName: dir,
Title: parts[this.directory.length],
ContentType: 'D', // indicates directory
Season: 0,
Episode: 0,
Length: 0
});
prior = dir;
}
}
}
}
);
}
this.refreshing = false;
showAllChange() {
this.refreshing = true;
setTimeout(() => this.reLoadVideos(), 100);
}

URLencode(x: string): string {
Expand All @@ -132,12 +126,12 @@ export class VideosComponent implements OnInit {

onDirectory(subdir: string) {
this.directory.push(subdir);
this.filterVideos();
this.reLoadVideos();
}

breadCrumb(ix: number) {
this.directory.length = ix;
this.filterVideos();
this.reLoadVideos();
}

showMenu(video: VideoMetadataInfo, event: any) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export interface GetVideoListRequest {
Descending?: boolean;
StartIndex?: number;
Count?: number;
CollapseSubDirs?: boolean;
}

export interface UpdateVideoMetadataRequest {
Expand Down

0 comments on commit 1f06b21

Please sign in to comment.