chore: update Angular cache and TypeScript build info

This commit is contained in:
2025-09-16 12:02:05 -04:00
parent b835bfcdbd
commit 3d78d0aef9
8 changed files with 515 additions and 31 deletions
@@ -1,4 +1,5 @@
import { ChangeDetectionStrategy, Component, ElementRef, ViewChild, input } from '@angular/core';
import { ChangeDetectionStrategy, Component, ElementRef, ViewChild, input, OnDestroy, AfterViewInit } from '@angular/core';
import { HistoryService } from '../../services/history.service';
@Component({
selector: 'app-video-player',
@@ -6,8 +7,12 @@ import { ChangeDetectionStrategy, Component, ElementRef, ViewChild, input } from
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class VideoPlayerComponent {
export class VideoPlayerComponent implements AfterViewInit, OnDestroy {
videoSource = input.required<string>();
// Optional resume position (in seconds)
startPositionSeconds = input<number | null>(null);
// Watch history id to update progress
watchHistoryId = input<string | null>(null);
@ViewChild('player', { static: false }) playerRef?: ElementRef<HTMLVideoElement>;
@@ -15,6 +20,90 @@ export class VideoPlayerComponent {
return this.playerRef?.nativeElement ?? null;
}
private progressTimer: any = null;
private lastSent = 0;
constructor(private history: HistoryService) {}
ngAfterViewInit(): void {
const v = this.el; if (!v) return;
// Seek to saved position when metadata is ready
v.addEventListener('loadedmetadata', () => {
const pos = this.startPositionSeconds();
if (typeof pos === 'number' && pos > 0 && isFinite(pos)) {
try { v.currentTime = Math.min(Math.max(pos, 0), v.duration || pos); } catch {}
}
});
// Start periodic progress updates when playing
v.addEventListener('play', () => {
// Ensure we seek on first play if metadata event already fired
const pos = this.startPositionSeconds();
if (typeof pos === 'number' && pos > 0 && isFinite(pos)) {
try {
if (!isNaN(v.currentTime) && v.currentTime < Math.max(0, pos - 1)) v.currentTime = pos;
} catch {}
}
this.startProgressLoop();
});
v.addEventListener('pause', () => this.sendProgressOnce());
v.addEventListener('ended', () => { this.sendProgressOnce(true); this.stopProgressLoop(); });
// As a safeguard, send progress on visibility change/unload
document.addEventListener('visibilitychange', this.onVisibilityChange);
window.addEventListener('beforeunload', this.onBeforeUnload);
}
ngOnDestroy(): void {
this.stopProgressLoop();
document.removeEventListener('visibilitychange', this.onVisibilityChange);
window.removeEventListener('beforeunload', this.onBeforeUnload);
}
private onVisibilityChange = () => {
if (document.hidden) this.sendProgressOnce();
};
private onBeforeUnload = () => {
this.sendProgressOnce();
};
private startProgressLoop(): void {
this.stopProgressLoop();
this.progressTimer = setInterval(() => this.sendProgressDebounced(), 4000);
}
private stopProgressLoop(): void {
if (this.progressTimer) {
clearInterval(this.progressTimer);
this.progressTimer = null;
}
}
private currentPos(): number {
const v = this.el; if (!v) return 0;
try { return Math.max(0, Math.floor(v.currentTime || 0)); } catch { return 0; }
}
private sendProgressDebounced(): void {
const now = Date.now();
if (now - this.lastSent < 3500) return;
this.lastSent = now;
this.sendProgress(false);
}
private sendProgressOnce(final: boolean = false): void {
this.sendProgress(final);
}
private sendProgress(final: boolean): void {
const id = this.watchHistoryId();
if (!id) return;
const pos = this.currentPos();
this.history.updateWatchProgress(id, pos, pos).subscribe({ next: () => {}, error: () => {} });
if (final) this.lastSent = Date.now();
}
togglePlayPause(): void {
const v = this.el; if (!v) return;
if (v.paused) v.play(); else v.pause();