chore: update Angular cache files and TypeScript build info
This commit is contained in:
parent
3dbfb04b15
commit
f3a78b7d7e
2
.angular/cache/20.2.2/app/.tsbuildinfo
vendored
2
.angular/cache/20.2.2/app/.tsbuildinfo
vendored
File diff suppressed because one or more lines are too long
BIN
db/newtube.db
BIN
db/newtube.db
Binary file not shown.
@ -73,6 +73,7 @@ CREATE TABLE IF NOT EXISTS watch_history (
|
||||
provider TEXT NOT NULL,
|
||||
video_id TEXT NOT NULL,
|
||||
title TEXT,
|
||||
thumbnail TEXT,
|
||||
watched_at TEXT NOT NULL,
|
||||
progress_seconds INTEGER DEFAULT 0,
|
||||
duration_seconds INTEGER DEFAULT 0,
|
||||
@ -145,5 +146,6 @@ CREATE TABLE IF NOT EXISTS video_tags (
|
||||
provider TEXT NOT NULL,
|
||||
video_id TEXT NOT NULL,
|
||||
tag_id TEXT NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
|
||||
created_at TEXT NOT NULL,
|
||||
PRIMARY KEY (user_id, provider, video_id, tag_id)
|
||||
);
|
||||
|
@ -279,11 +279,17 @@ function ensureTag(userId, name) {
|
||||
return id;
|
||||
}
|
||||
|
||||
export function likeVideo({ userId, provider, videoId }) {
|
||||
export function likeVideo({ userId, provider, videoId, title, thumbnail }) {
|
||||
const tagId = ensureTag(userId, 'like');
|
||||
// Upsert-like behavior; ignore if exists
|
||||
db.prepare(`INSERT OR IGNORE INTO video_tags (user_id, provider, video_id, tag_id) VALUES (?, ?, ?, ?)`)
|
||||
.run(userId, provider, videoId, tagId);
|
||||
db.prepare(`INSERT OR IGNORE INTO video_tags (user_id, provider, video_id, tag_id, created_at) VALUES (?, ?, ?, ?, ?)`)
|
||||
.run(userId, provider, videoId, tagId, nowIso());
|
||||
|
||||
// Also update the watch_history table with the title and thumbnail
|
||||
if (title || thumbnail) {
|
||||
upsertWatchHistory({ userId, provider, videoId, title, thumbnail });
|
||||
}
|
||||
|
||||
return { provider, video_id: videoId };
|
||||
}
|
||||
|
||||
@ -350,6 +356,7 @@ export function listLikedVideos({ userId, limit = 100, q }) {
|
||||
SELECT
|
||||
vt.provider,
|
||||
vt.video_id,
|
||||
vt.created_at,
|
||||
COALESCE(wh.title, '') AS title,
|
||||
COALESCE(wh.thumbnail, '') AS thumbnail,
|
||||
wh.last_watched_at AS last_watched_at
|
||||
@ -361,7 +368,7 @@ export function listLikedVideos({ userId, limit = 100, q }) {
|
||||
WHERE vt.user_id = ? AND vt.tag_id = ?
|
||||
`;
|
||||
const orderLimit = `
|
||||
ORDER BY COALESCE(wh.last_watched_at, wh.watched_at) DESC
|
||||
ORDER BY vt.created_at DESC
|
||||
LIMIT ?
|
||||
`;
|
||||
const query = hasQ
|
||||
|
@ -1058,10 +1058,36 @@ r.get('/user/likes', authMiddleware, (req, res) => {
|
||||
return res.json(rows);
|
||||
});
|
||||
|
||||
r.post('/user/likes', authMiddleware, (req, res) => {
|
||||
const { provider, videoId } = req.body || {};
|
||||
r.post('/user/likes', authMiddleware, async (req, res) => {
|
||||
let { provider, videoId, title, thumbnail } = req.body || {};
|
||||
try {
|
||||
console.log('[POST /user/likes] payload:', {
|
||||
provider,
|
||||
videoId,
|
||||
titlePreview: typeof title === 'string' ? title.slice(0, 80) : title,
|
||||
hasThumbnail: Boolean(thumbnail)
|
||||
});
|
||||
} catch {}
|
||||
if (!provider || !videoId) return res.status(400).json({ error: 'provider and videoId are required' });
|
||||
const row = likeVideo({ userId: req.user.id, provider, videoId });
|
||||
|
||||
// Server-side enrichment: if title or thumbnail is missing, fetch minimal details via yt-dlp
|
||||
try {
|
||||
const needTitle = !(typeof title === 'string' && title.trim().length > 0);
|
||||
const needThumb = !(typeof thumbnail === 'string' && thumbnail.trim().length > 0);
|
||||
if (needTitle || needThumb) {
|
||||
const url = providerUrlFrom(provider, videoId, { instance: req.query.instance, slug: req.query.slug, sourceUrl: req.query.sourceUrl });
|
||||
try {
|
||||
const raw = await youtubedl(url, { dumpSingleJson: true, noWarnings: true, noCheckCertificates: true, skipDownload: true });
|
||||
const meta = (typeof raw === 'string') ? JSON.parse(raw || '{}') : (raw || {});
|
||||
if (needTitle) title = meta?.title || title || '';
|
||||
if (needThumb) thumbnail = meta?.thumbnail || (Array.isArray(meta?.thumbnails) && meta.thumbnails.length ? meta.thumbnails[0].url : thumbnail || '');
|
||||
} catch (e) {
|
||||
console.warn('[POST /user/likes] details fetch failed, continuing without enrichment:', e?.message || e);
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
|
||||
const row = likeVideo({ userId: req.user.id, provider, videoId, title, thumbnail });
|
||||
return res.status(201).json(row);
|
||||
});
|
||||
|
||||
|
@ -28,7 +28,7 @@
|
||||
{{ video.duration / 60 | number:'1.0-0' }}:{{ (video.duration % 60) | number:'2.0-0' }}
|
||||
</div>
|
||||
<div class="absolute bottom-2 left-2">
|
||||
<app-like-button [videoId]="video.videoId" [provider]="'youtube'"></app-like-button>
|
||||
<app-like-button [videoId]="video.videoId" [provider]="instances.selectedProvider()" [title]="video.title" [thumbnail]="video.thumbnail"></app-like-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4 flex-grow flex flex-col">
|
||||
|
@ -25,7 +25,7 @@ import { LikeButtonComponent } from '../shared/components/like-button/like-butto
|
||||
})
|
||||
export class HomeComponent {
|
||||
private apiService = inject(YoutubeApiService);
|
||||
private instances = inject(InstanceService);
|
||||
instances = inject(InstanceService);
|
||||
|
||||
trendingVideos = signal<Video[]>([]);
|
||||
loading = signal(true);
|
||||
|
@ -55,7 +55,7 @@
|
||||
{{ formatViews(video.views) }} en direct
|
||||
</div>
|
||||
<div class="absolute bottom-2 left-2">
|
||||
<app-like-button [videoId]="video.videoId" [provider]="'twitch'"></app-like-button>
|
||||
<app-like-button [videoId]="video.videoId" [provider]="'twitch'" [title]="video.title" [thumbnail]="video.thumbnail"></app-like-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4 flex-grow flex flex-col">
|
||||
@ -91,7 +91,7 @@
|
||||
{{ video.duration / 60 | number:'1.0-0' }}:{{ (video.duration % 60) | number:'2.0-0' }}
|
||||
</div>
|
||||
<div class="absolute bottom-2 left-2">
|
||||
<app-like-button [videoId]="video.videoId" [provider]="'twitch'"></app-like-button>
|
||||
<app-like-button [videoId]="video.videoId" [provider]="'twitch'" [title]="video.title" [thumbnail]="video.thumbnail"></app-like-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4 flex-grow flex flex-col">
|
||||
@ -127,7 +127,7 @@
|
||||
{{ video.duration / 60 | number:'1.0-0' }}:{{ (video.duration % 60) | number:'2.0-0' }}
|
||||
</div>
|
||||
<div class="absolute bottom-2 left-2">
|
||||
<app-like-button [videoId]="video.videoId" [provider]="selectedProviderForView()"></app-like-button>
|
||||
<app-like-button [videoId]="video.videoId" [provider]="selectedProviderForView()" [title]="video.title" [thumbnail]="video.thumbnail"></app-like-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4 flex-grow flex flex-col">
|
||||
|
@ -82,6 +82,8 @@ export class LikeButtonComponent implements OnInit, OnDestroy, OnChanges {
|
||||
private auth = inject(AuthService);
|
||||
|
||||
@Input() provider = 'youtube';
|
||||
@Input() title?: string;
|
||||
@Input() thumbnail?: string;
|
||||
|
||||
private _videoId = '';
|
||||
|
||||
@ -193,7 +195,10 @@ export class LikeButtonComponent implements OnInit, OnDestroy, OnChanges {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.likesService.like(this.provider, this.videoId)
|
||||
const cleanTitle = (this.title || '').trim();
|
||||
const cleanThumb = (this.thumbnail || '').trim();
|
||||
try { console.debug('[LikeButton] like payload', { provider: this.provider, videoId: this.videoId, title: cleanTitle, hasThumb: !!cleanThumb }); } catch {}
|
||||
this.likesService.like(this.provider, this.videoId, cleanTitle || undefined, cleanThumb || undefined)
|
||||
.pipe(
|
||||
finalize(() => {
|
||||
if (!this.destroyRef) {
|
||||
|
@ -80,7 +80,7 @@
|
||||
<div class="relative">
|
||||
<img [src]="v.thumbnail" [alt]="v.title" loading="lazy" decoding="async" class="w-full h-32 object-cover">
|
||||
<div class="absolute bottom-2 left-2">
|
||||
<app-like-button [videoId]="v.videoId" [provider]="provider()"></app-like-button>
|
||||
<app-like-button [videoId]="v.videoId" [provider]="provider()" [title]="v.title" [thumbnail]="v.thumbnail"></app-like-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-2 text-sm line-clamp-2">{{ v.title }}</div>
|
||||
@ -131,7 +131,7 @@
|
||||
<div class="relative">
|
||||
<img [src]="v.thumbnail" [alt]="v.title" loading="lazy" decoding="async" class="w-full h-32 object-cover">
|
||||
<div class="absolute bottom-2 left-2">
|
||||
<app-like-button [videoId]="v.videoId" [provider]="provider()"></app-like-button>
|
||||
<app-like-button [videoId]="v.videoId" [provider]="provider()" [title]="v.title" [thumbnail]="v.thumbnail"></app-like-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-2 text-sm line-clamp-2">{{ v.title }}</div>
|
||||
|
@ -33,7 +33,7 @@
|
||||
{{ v.duration / 60 | number:'1.0-0' }}:{{ (v.duration % 60) | number:'2.0-0' }}
|
||||
</div>
|
||||
<div class="absolute bottom-2 left-2">
|
||||
<app-like-button [videoId]="v.videoId" [provider]="b.provider"></app-like-button>
|
||||
<app-like-button [videoId]="v.videoId" [provider]="b.provider" [title]="v.title" [thumbnail]="v.thumbnail"></app-like-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-3">
|
||||
|
@ -8,6 +8,7 @@ export interface LikedVideoItem {
|
||||
title?: string;
|
||||
thumbnail?: string;
|
||||
last_watched_at?: string | null;
|
||||
created_at?: string;
|
||||
}
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
@ -29,10 +30,10 @@ export class LikesService {
|
||||
return this.http.get<LikedVideoItem[]>(`${this.apiBase()}/user/likes`, { params, withCredentials: true });
|
||||
}
|
||||
|
||||
like(provider: string, videoId: string): Observable<{ provider: string; video_id: string }> {
|
||||
like(provider: string, videoId: string, title?: string, thumbnail?: string): Observable<{ provider: string; video_id: string }> {
|
||||
return this.http.post<{ provider: string; video_id: string }>(
|
||||
`${this.apiBase()}/user/likes`,
|
||||
{ provider, videoId },
|
||||
{ provider, videoId, title, thumbnail },
|
||||
{ withCredentials: true }
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user