chore: update Angular cache and TypeScript build info files

This commit is contained in:
2025-09-21 11:37:04 -04:00
parent 2420140cad
commit 80935ebd74
5 changed files with 191 additions and 61 deletions
+79 -9
View File
@@ -376,15 +376,14 @@ export class YoutubeApiService {
if (!key) return of({ items: [], nextCursor: null });
const params = new URLSearchParams({
type: 'video',
part: 'snippet,contentDetails,statistics',
// search.list only supports 'snippet'
part: 'snippet',
// Optimize for quota usage: reduce initial results
maxResults: '12',
q: String(query),
regionCode: String(region),
// Add safeSearch for better content filtering
safeSearch: 'moderate',
// Video category filter for better relevance
videoCategoryId: '0', // All categories
key: String(key),
});
// Prefer results matching user's language when available
@@ -569,6 +568,7 @@ export class YoutubeApiService {
url: `/watch/${id}`,
videoId: id,
type: 'video',
provider: 'youtube',
title: sn.title || '',
thumbnail: thumb,
uploaderName: sn.channelTitle || '',
@@ -590,6 +590,7 @@ export class YoutubeApiService {
url: `/watch/${id}`,
videoId: id,
type: 'video',
provider: 'youtube',
title: sn.title || '',
thumbnail: thumb,
uploaderName: sn.channelTitle || '',
@@ -607,6 +608,7 @@ export class YoutubeApiService {
url: `/watch/${i.id}`,
videoId: i.id || '',
type: 'video',
provider: 'dailymotion',
title: i.title || '',
thumbnail: i.thumbnail_720_url || i.thumbnail_480_url || i.thumbnail_url || '',
uploaderName: i['owner.screenname'] || '',
@@ -634,6 +636,7 @@ export class YoutubeApiService {
url: `https://${instance}/w/${i.uuid}`,
videoId: i.uuid || '',
type: 'video',
provider: 'peertube',
title: i.name || '',
thumbnail,
uploaderName,
@@ -664,6 +667,7 @@ export class YoutubeApiService {
url: webUrl,
videoId: i?.claim_id || i?.txid || i?.claimId || i?.id || '',
type: 'video',
provider: 'odysee',
title: val?.title || i?.name || '',
thumbnail: thumb,
uploaderName,
@@ -702,6 +706,7 @@ export class YoutubeApiService {
url,
videoId: id,
type: 'video',
provider: 'rumble',
title,
thumbnail: thumb,
uploaderName,
@@ -719,6 +724,7 @@ export class YoutubeApiService {
url: `/watch/${i.id}`,
videoId: i.id || '',
type: 'video',
provider: 'twitch',
title: i.title || '',
thumbnail: i.thumbnail_url?.replace('%{width}', '640').replace('%{height}', '360') || '',
uploaderName: i.user_name || '',
@@ -735,6 +741,7 @@ export class YoutubeApiService {
url: `https://www.twitch.tv/${i.user_login}`,
videoId: i.user_login || i.id || '',
type: 'channel',
provider: 'twitch',
title: i.title || '',
thumbnail: (i.thumbnail_url || '').replace('{width}', '640').replace('{height}', '360'),
uploaderName: i.user_name || '',
@@ -750,17 +757,80 @@ export class YoutubeApiService {
url: `https://www.twitch.tv/${i.broadcaster_login || i.user_login || ''}`,
videoId: i.broadcaster_login || i.user_login || '',
type: 'channel',
title: i.display_name || i.title || '',
provider: 'twitch',
title: i.title || i.display_name || i.broadcaster_login || i.user_name || '',
thumbnail: (i.thumbnail_url || '').replace('{width}', '640').replace('{height}', '360'),
uploaderName: i.display_name || i.broadcaster_login || i.user_login || '',
uploaderName: i.display_name || i.broadcaster_name || i.user_name || '',
uploaderUrl: `https://www.twitch.tv/${i.broadcaster_login || i.user_login || ''}`,
uploaderAvatar: (i.thumbnail_url || '').replace('{width}', '70').replace('{height}', '70'),
uploadedDate: i.started_at || '',
duration: 0, // Not a VOD
views: 0, // Not applicable
uploaded: i.started_at ? Date.parse(i.started_at) : 0,
uploadedDate: '',
duration: 0,
views: 0,
uploaded: 0,
});
// Twitch Clips mapper (Shorts-equivalent)
private mapTwitchClipToVideo = (i: any): Video => ({
url: i.url || `https://clips.twitch.tv/${i.id}`,
videoId: i.id || '',
type: 'video',
provider: 'twitch',
kind: 'clip',
title: i.title || '',
// Clip thumbnails often have width/height placeholders or fixed preview sizes
thumbnail: (i.thumbnail_url || '').replace('%{width}', '640').replace('%{height}', '360'),
uploaderName: i.broadcaster_name || i.creator_name || '',
uploaderUrl: i.broadcaster_name ? `https://www.twitch.tv/${i.broadcaster_name}` : '',
uploaderAvatar: '',
uploadedDate: i.created_at || '',
duration: Number(i.duration || 0),
views: Number(i.view_count || 0),
uploaded: i.created_at ? Date.parse(i.created_at) : 0,
});
/**
* Search Twitch Clips by first finding channels for a query (or recent popular channels when empty),
* then fetching recent clips per broadcaster. Returns up to ~24 clips with pagination via channel search cursor.
*/
public searchTwitchClipsPage(query: string, cursor?: string | null): Observable<PagedResult<Video>> {
return this.getTwitchAuthToken().pipe(
switchMap(token => {
if (!token) return of({ items: [], nextCursor: null });
const headers = new HttpHeaders({ 'Client-ID': this.resolveTwitchClientId()!, 'Authorization': `Bearer ${token}` });
const after = cursor ? `&after=${encodeURIComponent(cursor)}` : '';
const q = String(query || '');
const chPrimary = `/api/twitch-api/helix/search/channels?query=${encodeURIComponent(q)}&first=10${after}`;
const chFallback = `/proxy/twitch-api/helix/search/channels?query=${encodeURIComponent(q)}&first=10${after}`;
return this.http.get<any>(chPrimary, { headers }).pipe(
catchError(() => this.http.get<any>(chFallback, { headers })),
switchMap(chRes => {
const channels = Array.isArray(chRes?.data) ? chRes.data : [];
const userIds: string[] = channels.map((c: any) => c?.id).filter(Boolean).slice(0, 10);
if (userIds.length === 0) return of({ items: [], nextCursor: null } as PagedResult<Video>);
const since = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
const perUserRequests = userIds.map(uid => {
const cPrimary = `/api/twitch-api/helix/clips?broadcaster_id=${encodeURIComponent(uid)}&first=5&started_at=${encodeURIComponent(since)}`;
const cFallback = `/proxy/twitch-api/helix/clips?broadcaster_id=${encodeURIComponent(uid)}&first=5&started_at=${encodeURIComponent(since)}`;
return this.http.get<any>(cPrimary, { headers }).pipe(
catchError(() => this.http.get<any>(cFallback, { headers })),
map(res => Array.isArray(res?.data) ? res.data : [])
);
});
return forkJoin(perUserRequests).pipe(
map((lists: any[][]) => lists.flat().slice(0, 24)),
map((clips: any[]) => ({
items: clips.map((i: any) => this.mapTwitchClipToVideo(i)),
nextCursor: chRes?.pagination?.cursor || null,
} as PagedResult<Video>)),
catchError(() => of({ items: [], nextCursor: null } as PagedResult<Video>))
);
}),
catchError((e) => { console.error('Twitch clips search error', e); return of({ items: [], nextCursor: null }); })
);
})
);
}
private parseISODurationToSeconds(iso: string): number {
// ... (rest of the code remains the same)
const m = /^PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?$/i.exec(iso || '');