chore: update Angular build cache and TypeScript definitions

This commit is contained in:
2025-09-15 22:34:25 -04:00
parent 23c9ec539e
commit 49f9018e77
16 changed files with 1118 additions and 153 deletions
@@ -1,6 +1,92 @@
<div class="container mx-auto p-4 sm:p-6">
<h2 class="text-3xl font-bold mb-6 text-slate-100 border-l-4 border-red-500 pl-4">Vidéos que vous aimez</h2>
<div class="container mx-auto p-4 sm:p-6 max-w-6xl">
<h2 class="text-3xl font-bold mb-6 text-slate-100 border-l-4 border-red-500 pl-4 flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="h-7 w-7 mr-2 text-red-400">
<path d="M12 21s-6.716-4.248-9.193-6.725A6 6 0 0 1 12 5.414 6 6 0 0 1 21.193 14.275C18.716 16.752 12 21 12 21z" />
</svg>
Liked videos
</h2>
<div class="bg-slate-800/50 rounded-lg p-6 border border-slate-700">
<p class="text-slate-300">Ici apparaîtront vos vidéos aimées (tous providers). Fonctionnalité à venir.</p>
<!-- Barre de recherche -->
<div class="mb-4 relative">
<input
type="text"
[ngModel]="searchQuery()"
(ngModelChange)="onSearchChange($event)"
class="block w-full pl-10 pr-10 py-2 border border-slate-600 rounded-lg bg-slate-700 text-slate-100 placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition duration-150"
placeholder="Rechercher dans les vidéos aimées..."
/>
<svg class="w-5 h-5 absolute left-3 top-1/2 -translate-y-1/2 text-slate-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
<button
*ngIf="searchQuery()"
(click)="clearSearch()"
class="absolute right-2 top-1/2 -translate-y-1/2 px-2 py-1 text-xs rounded bg-slate-600 hover:bg-slate-500 text-white"
aria-label="Effacer la recherche"
>
Effacer
</button>
</div>
@if (loading()) {
<div class="text-slate-400">Chargement…</div>
} @else if (error(); as err) {
<div class="text-red-300">{{ err }}</div>
} @else if (items().length === 0) {
<div class="text-slate-400">Aucune vidéo aimée pour le moment.</div>
} @else {
<ul class="space-y-3">
@for (it of items(); track it.provider + ':' + it.video_id) {
<li class="group bg-slate-700/50 hover:bg-slate-700/80 rounded-lg overflow-hidden transition-all">
<a [routerLink]="['/watch', it.video_id]" [queryParams]="{ p: it.provider }" class="block p-4 hover:no-underline">
<div class="flex items-center">
<!-- Miniature de la vidéo -->
<div class="relative flex-shrink-0 w-24 h-16 bg-slate-800 rounded overflow-hidden mr-4">
@if (it.thumbnail) {
<img [src]="it.thumbnail" alt="" class="w-full h-full object-cover" />
} @else {
<div class="w-full h-full flex items-center justify-center bg-gradient-to-br from-slate-700 to-slate-800">
<span class="inline-flex items-center px-2 py-0.5 rounded border text-xs"
[ngStyle]="getProviderColors(it.provider)">
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
</svg>
{{ getProviderDisplayName(it.provider) }}
</span>
</div>
}
</div>
<!-- Détails de la vidéo -->
<div class="flex-1 min-w-0">
<div class="text-slate-100 font-medium group-hover:text-red-400 truncate">
{{ it.title || (it.provider + ':' + it.video_id) }}
</div>
<div class="text-xs text-slate-400 mt-1">
<span class="inline-flex items-center gap-2">
<span class="inline-flex items-center px-2 py-0.5 rounded border"
[ngStyle]="getProviderColors(it.provider)">
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
</svg>
{{ getProviderDisplayName(it.provider) }}
</span>
@if (it.last_watched_at) {
<span>Vu le {{ it.last_watched_at | date:'short' }}</span>
}
</span>
</div>
</div>
<!-- Flèche -->
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 flex-shrink-0 text-slate-400 group-hover:text-slate-200 ml-4" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
</div>
</a>
</li>
}
</ul>
}
</div>
</div>
@@ -1,11 +1,93 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterLink } from '@angular/router';
import { FormsModule } from '@angular/forms';
import { LikesService, LikedVideoItem } from '../../../services/likes.service';
@Component({
selector: 'app-library-liked',
standalone: true,
imports: [CommonModule],
imports: [CommonModule, RouterLink, FormsModule],
templateUrl: './liked.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class LikedComponent { }
export class LikedComponent {
private likes = inject(LikesService);
loading = signal<boolean>(true);
error = signal<string | null>(null);
items = signal<LikedVideoItem[]>([]);
searchQuery = signal<string>('');
constructor() {
this.refresh();
}
refresh(q?: string): void {
this.loading.set(true);
this.error.set(null);
this.likes.list(200, q).subscribe({
next: (rows) => {
this.items.set(rows || []);
this.loading.set(false);
},
error: (err) => {
const msg = err?.error?.error || err?.message || 'Erreur lors du chargement des vidéos aimées';
this.error.set(String(msg));
this.loading.set(false);
}
});
}
onSearchChange(value: string) {
const q = (value || '').trim();
this.searchQuery.set(value);
this.refresh(q || undefined);
}
clearSearch() {
this.searchQuery.set('');
this.refresh();
}
// --- Provider helpers (match History/Header badges)
private formatProviderName(providerId: string): string {
const map: Record<string, string> = {
youtube: 'YouTube',
vimeo: 'Vimeo',
dailymotion: 'Dailymotion',
peertube: 'PeerTube',
rumble: 'Rumble',
twitch: 'Twitch',
odysee: 'Odysee',
};
return map[(providerId || '').toLowerCase()] || providerId;
}
getProviderDisplayName(providerId: string | null | undefined): string {
if (!providerId) return '';
return this.formatProviderName(providerId);
}
getProviderColors(providerId: string | null | undefined): { [key: string]: string } {
const id = (providerId || '').toLowerCase();
switch (id) {
case 'youtube':
return { backgroundColor: 'rgba(220, 38, 38, 0.15)', color: 'rgb(248, 113, 113)', borderColor: 'rgba(239, 68, 68, 0.3)' };
case 'vimeo':
return { backgroundColor: 'rgba(2, 132, 199, 0.15)', color: 'rgb(125, 211, 252)', borderColor: 'rgba(14, 165, 233, 0.3)' };
case 'dailymotion':
return { backgroundColor: 'rgba(37, 99, 235, 0.15)', color: 'rgb(147, 197, 253)', borderColor: 'rgba(59, 130, 246, 0.3)' };
case 'peertube':
return { backgroundColor: 'rgba(245, 158, 11, 0.15)', color: 'rgb(252, 211, 77)', borderColor: 'rgba(245, 158, 11, 0.3)' };
case 'rumble':
return { backgroundColor: 'rgba(22, 163, 74, 0.15)', color: 'rgb(134, 239, 172)', borderColor: 'rgba(34, 197, 94, 0.3)' };
case 'twitch':
return { backgroundColor: 'rgba(168, 85, 247, 0.15)', color: 'rgb(216, 180, 254)', borderColor: 'rgba(168, 85, 247, 0.3)' };
case 'odysee':
return { backgroundColor: 'rgba(236, 72, 153, 0.15)', color: 'rgb(251, 207, 232)', borderColor: 'rgba(236, 72, 153, 0.3)' };
default:
return { backgroundColor: 'rgba(30, 41, 59, 0.8)', color: 'rgb(203, 213, 225)', borderColor: 'rgb(51, 65, 85)' };
}
}
}