chore: update TypeScript build info cache
This commit is contained in:
@@ -6,7 +6,6 @@ import { InstanceService, Provider } from '../../services/instance.service';
|
||||
import { ThemesService } from '../../services/themes.service';
|
||||
import { YoutubeApiService } from '../../services/youtube-api.service';
|
||||
import { Video } from '../../models/video.model';
|
||||
import { InfiniteAnchorComponent } from '../shared/infinite-anchor/infinite-anchor.component';
|
||||
import { TranslatePipe } from '../../pipes/translate.pipe';
|
||||
import { LikeButtonComponent } from '../shared/components/like-button/like-button.component';
|
||||
|
||||
@@ -25,7 +24,7 @@ interface SectionState {
|
||||
standalone: true,
|
||||
templateUrl: './provider-theme-page.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [CommonModule, FormsModule, RouterLink, InfiniteAnchorComponent, TranslatePipe, LikeButtonComponent]
|
||||
imports: [CommonModule, FormsModule, RouterLink, TranslatePipe, LikeButtonComponent]
|
||||
})
|
||||
export class ProviderThemePageComponent implements OnDestroy {
|
||||
private route = inject(ActivatedRoute);
|
||||
@@ -53,7 +52,12 @@ export class ProviderThemePageComponent implements OnDestroy {
|
||||
|
||||
// Sections (Live removed on provider pages)
|
||||
recorded: SectionState = { key: 'recorded', titleKey: 'themes.recorded', items: [], nextCursor: null, loading: false, error: null, visibleCount: 20 };
|
||||
videos: SectionState = { key: 'videos', titleKey: 'themes.videos', items: [], nextCursor: null, loading: false, error: null, visibleCount: 20 };
|
||||
videos: SectionState = { key: 'videos', titleKey: 'themes.videos', items: [], nextCursor: null, loading: false, error: null, visibleCount: 12 };
|
||||
|
||||
// Derived lists for UI separation
|
||||
shorts: Video[] = []; // aggregated from both sections
|
||||
nonShorts: Video[] = []; // from videos section only
|
||||
recordedNonShorts: Video[] = []; // from recorded section
|
||||
|
||||
constructor() {
|
||||
this.route.paramMap.subscribe(pm => {
|
||||
@@ -86,9 +90,12 @@ export class ProviderThemePageComponent implements OnDestroy {
|
||||
const apply = () => {
|
||||
// Reset visible window to ensure top results reflect new sort/filter instantly
|
||||
this.recorded.visibleCount = Math.max(20, this.recorded.visibleCount);
|
||||
this.videos.visibleCount = Math.max(20, this.videos.visibleCount);
|
||||
// Keep videos at 12-increment pagination
|
||||
this.videos.visibleCount = Math.max(12, this.videos.visibleCount);
|
||||
this.recorded.items = this.applyFilters(this.recorded.items);
|
||||
this.videos.items = this.applyFilters(this.videos.items);
|
||||
// Recompute derived partitions
|
||||
this.partitionVideos();
|
||||
// After filtering, re-evaluate if we need to prefetch more
|
||||
this.checkPrefetch(this.recorded);
|
||||
this.checkPrefetch(this.videos);
|
||||
@@ -104,8 +111,12 @@ export class ProviderThemePageComponent implements OnDestroy {
|
||||
|
||||
private resetAll() {
|
||||
for (const s of [this.recorded, this.videos]) {
|
||||
s.items = []; s.nextCursor = null; s.loading = false; s.error = null; s.visibleCount = 20;
|
||||
s.items = []; s.nextCursor = null; s.loading = false; s.error = null;
|
||||
s.visibleCount = s.key === 'videos' ? 12 : 20;
|
||||
}
|
||||
this.shorts = [];
|
||||
this.nonShorts = [];
|
||||
this.recordedNonShorts = [];
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
@@ -175,6 +186,125 @@ export class ProviderThemePageComponent implements OnDestroy {
|
||||
return arr;
|
||||
}
|
||||
|
||||
// Pagination des shorts
|
||||
shortsVisibleCount = 20;
|
||||
|
||||
// Charge plus de shorts
|
||||
showMoreShorts() {
|
||||
this.shortsVisibleCount += 20;
|
||||
// Vérifier si nous avons assez de vidéos pour afficher
|
||||
if (this.shorts.length <= this.shortsVisibleCount) {
|
||||
// Si nous n'avons pas assez de vidéos, essayer d'en charger plus
|
||||
this.checkAndLoadMoreShorts();
|
||||
}
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
// Vérifie et charge plus de shorts si nécessaire
|
||||
private checkAndLoadMoreShorts() {
|
||||
// Vérifier si nous avons déjà chargé toutes les vidéos disponibles
|
||||
const totalShorts = this.videos.items.filter(v => this.isShortItem(v)).length +
|
||||
this.recorded.items.filter(v => this.isShortItem(v)).length;
|
||||
|
||||
// Si nous avons moins de vidéos que ce que nous voulons afficher, essayer d'en charger plus
|
||||
if (totalShorts <= this.shortsVisibleCount) {
|
||||
// Essayer de charger plus de vidéos de la section videos d'abord
|
||||
if (this.videos.nextCursor && !this.videos.loading) {
|
||||
this.loadMore(this.videos);
|
||||
}
|
||||
// Puis essayer de charger plus de vidéos de la section recorded
|
||||
else if (this.recorded.nextCursor && !this.recorded.loading) {
|
||||
this.loadMore(this.recorded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Classification per spec
|
||||
private isShortItem(x: any): boolean {
|
||||
try {
|
||||
if (!x) return false;
|
||||
if (x.type === 'short') return true;
|
||||
if ((x as any).isShort === true) return true;
|
||||
// Platform URL hints (best-effort)
|
||||
const u = String((x as any).url || '');
|
||||
if (u) {
|
||||
// YouTube shorts URLs: /shorts/VIDEOID
|
||||
if (/\/shorts\//i.test(u)) return true;
|
||||
// General reels/clips hints used by some providers
|
||||
if (/\b(reel|reels|clip|clips)\b/i.test(u)) return true;
|
||||
}
|
||||
const dur = Number((x as any).durationSeconds ?? x.duration ?? 0);
|
||||
if (dur > 0 && dur <= 60) return true;
|
||||
const ar = Number((x as any).aspectRatio ?? 0);
|
||||
if (ar > 1.2) return true; // treat vertical as shorts if hinted
|
||||
return false;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private partitionVideos() {
|
||||
const vids = this.videos.items || [];
|
||||
const recs = this.recorded.items || [];
|
||||
const shortsAgg: Video[] = [];
|
||||
const vidsLongs: Video[] = [];
|
||||
const recsLongs: Video[] = [];
|
||||
const seenShort = new Set<string>();
|
||||
|
||||
const pushShort = (x: Video) => {
|
||||
const k = x.videoId || x.url || '';
|
||||
if (k && !seenShort.has(k)) {
|
||||
seenShort.add(k);
|
||||
shortsAgg.push(x);
|
||||
}
|
||||
};
|
||||
|
||||
// Filtrer les shorts et les vidéos longues pour les vidéos
|
||||
for (const v of vids) {
|
||||
if (this.isShortItem(v)) {
|
||||
pushShort(v);
|
||||
} else {
|
||||
vidsLongs.push(v);
|
||||
}
|
||||
}
|
||||
|
||||
// Filtrer les shorts et les vidéos longues pour les enregistrements
|
||||
for (const r of recs) {
|
||||
if (this.isShortItem(r)) {
|
||||
pushShort(r);
|
||||
} else {
|
||||
recsLongs.push(r);
|
||||
}
|
||||
}
|
||||
|
||||
// Mettre à jour les tableaux de vidéos
|
||||
this.shorts = shortsAgg;
|
||||
|
||||
// Conserver les vidéos déjà affichées si elles existent encore
|
||||
const currentNonShorts = new Set(this.nonShorts.map(v => v.videoId || v.url || ''));
|
||||
const currentRecordedNonShorts = new Set(this.recordedNonShorts.map(v => v.videoId || v.url || ''));
|
||||
|
||||
// Mettre à jour les vidéos non-courtes en conservant l'ordre et les éléments existants
|
||||
this.nonShorts = [
|
||||
...this.nonShorts.filter(v => vidsLongs.some(v2 => (v2.videoId || v2.url) === (v.videoId || v.url))),
|
||||
...vidsLongs.filter(v => !currentNonShorts.has(v.videoId || v.url || ''))
|
||||
].slice(0, this.videos.visibleCount);
|
||||
|
||||
// Mettre à jour les enregistrements non-courts en conservant l'ordre et les éléments existants
|
||||
this.recordedNonShorts = [
|
||||
...this.recordedNonShorts.filter(v => recsLongs.some(v2 => (v2.videoId || v2.url) === (v.videoId || v.url))),
|
||||
...recsLongs.filter(v => !currentRecordedNonShorts.has(v.videoId || v.url || ''))
|
||||
].slice(0, this.recorded.visibleCount);
|
||||
|
||||
// Mettre à jour le compteur de shorts visibles si nécessaire
|
||||
if (this.shortsVisibleCount > this.shorts.length) {
|
||||
this.shortsVisibleCount = this.shorts.length;
|
||||
}
|
||||
|
||||
// Forcer la détection des changements
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
changeProvider(p: any) {
|
||||
const provider = String(p) as Provider;
|
||||
// Sync the global provider so header search reflects the current context immediately
|
||||
@@ -240,9 +370,21 @@ export class ProviderThemePageComponent implements OnDestroy {
|
||||
const afterKeys = existing.size;
|
||||
const added = Math.max(0, afterKeys - beforeKeys);
|
||||
const duplicates = Math.max(0, (incoming.length || 0) - added);
|
||||
|
||||
// Mettre à jour les éléments de la section
|
||||
section.items = this.applyFilters(merged);
|
||||
section.nextCursor = res.nextCursor || null;
|
||||
section.loading = false;
|
||||
|
||||
// Mettre à jour les tableaux de vidéos visibles
|
||||
if (section.key === 'videos') {
|
||||
this.nonShorts = section.items.filter(v => !this.isShortItem(v)).slice(0, section.visibleCount);
|
||||
} else if (section.key === 'recorded') {
|
||||
this.recordedNonShorts = section.items.filter(v => !this.isShortItem(v)).slice(0, section.visibleCount);
|
||||
}
|
||||
|
||||
// Mettre à jour les shorts si nécessaire
|
||||
this.partitionVideos();
|
||||
if (incoming.length === 0) {
|
||||
console.info('[ProviderTheme] Empty page', { provider, section: section.key, theme: this.theme(), query, cursor: section.nextCursor });
|
||||
}
|
||||
@@ -269,6 +411,7 @@ export class ProviderThemePageComponent implements OnDestroy {
|
||||
}
|
||||
// Re-apply filters/sorts that depend on duration
|
||||
section.items = this.applyFilters(section.items);
|
||||
this.partitionVideos();
|
||||
this.checkPrefetch(section);
|
||||
this.cdr.markForCheck();
|
||||
},
|
||||
@@ -277,6 +420,7 @@ export class ProviderThemePageComponent implements OnDestroy {
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
this.partitionVideos();
|
||||
// Logic-based prefetch: if we are within ~2 rows of the end, prefetch next page
|
||||
this.checkPrefetch(section);
|
||||
this.cdr.markForCheck();
|
||||
@@ -312,16 +456,30 @@ export class ProviderThemePageComponent implements OnDestroy {
|
||||
|
||||
// Increase visible items by 20; fetch the next page if we need more data
|
||||
showMore(section: SectionState) {
|
||||
const target = section.visibleCount + 20;
|
||||
const step = section.key === 'videos' ? 12 : 20;
|
||||
const target = section.visibleCount + step;
|
||||
section.visibleCount = target;
|
||||
// If we don't have enough items yet and there is a next page, fetch it
|
||||
if (section.items.length < target && section.nextCursor && !section.loading) {
|
||||
|
||||
// Mettre à jour les vidéos visibles immédiatement
|
||||
if (section.key === 'videos') {
|
||||
this.nonShorts = this.videos.items.slice(0, section.visibleCount);
|
||||
} else if (section.key === 'recorded') {
|
||||
this.recordedNonShorts = this.recorded.items.slice(0, section.visibleCount);
|
||||
}
|
||||
|
||||
// Si nous n'avons pas assez d'éléments et qu'il y a une page suivante, la charger
|
||||
const poolLength = this.poolLengthFor(section);
|
||||
if (poolLength < target && section.nextCursor && !section.loading) {
|
||||
console.debug('[ProviderTheme] showMore triggers fetch', { section: section.key, target, have: section.items.length, nextCursor: section.nextCursor });
|
||||
this.loadMore(section);
|
||||
}
|
||||
// Additionally, run logic-based prefetch check in case of edge conditions
|
||||
|
||||
// Vérifier également si nous devons charger plus de contenu
|
||||
this.checkPrefetch(section);
|
||||
this.cdr.markForCheck();
|
||||
|
||||
// Forcer la détection des changements pour mettre à jour l'interface utilisateur
|
||||
try { this.cdr.detectChanges(); } catch {}
|
||||
}
|
||||
|
||||
// Helpers for watch params
|
||||
@@ -363,13 +521,20 @@ export class ProviderThemePageComponent implements OnDestroy {
|
||||
private checkPrefetch(section: SectionState) {
|
||||
const cols = this.currentCols();
|
||||
const threshold = cols * 2; // ~2 rows
|
||||
const remaining = section.items.length - section.visibleCount;
|
||||
const pool = this.poolLengthFor(section);
|
||||
const remaining = pool - section.visibleCount;
|
||||
if (remaining <= threshold && section.nextCursor && !section.loading) {
|
||||
console.debug('[ProviderTheme] Logic prefetch triggered', { section: section.key, remaining, threshold, nextCursor: section.nextCursor });
|
||||
this.loadMore(section);
|
||||
}
|
||||
}
|
||||
|
||||
private poolLengthFor(section: SectionState): number {
|
||||
if (section.key === 'videos') return this.nonShorts.length || 0;
|
||||
if (section.key === 'recorded') return this.recordedNonShorts.length || 0;
|
||||
return section.items.length;
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
try { if (this.handleResize) window.removeEventListener('resize', this.handleResize); } catch {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user