feat(api): include rating and bookmark metadata in media queries

- Add bookmark_count, avg_rating, and star_count fields to photo, video, and text queries
- Join bookmarks and stars tables to aggregate counts and average ratings
- Use COALESCE to ensure default values for missing metadata are zero
- Update folder contents handler to use actual thumbnail path from database instead of conditional URLs
This commit is contained in:
tigeren 2025-10-12 09:03:17 +00:00
parent ce61ebbf9e
commit 335f47549a
5 changed files with 140 additions and 8 deletions

View File

@ -0,0 +1,88 @@
# Thumbnail Fix Summary
## 🐛 Issue Identified
The cluster folder view was generating 404 errors for video thumbnails:
```
GET /api/videos/97/thumbnail 404 in 624ms
GET /api/videos/96/thumbnail 404 in 631ms
```
## 🔍 Root Cause
The cluster folders API was hardcoding non-existent thumbnail URLs instead of using the actual thumbnail paths from the database:
**Before (Broken)**:
```typescript
thumbnail: mediaItem.type === 'video'
? `/api/videos/${mediaItem.id}/thumbnail` // ❌ This endpoint doesn't exist
: mediaItem.type === 'photo'
? `/api/photos/${mediaItem.id}/thumbnail` // ❌ This endpoint doesn't exist
: '',
```
**After (Fixed)**:
```typescript
thumbnail: mediaItem.thumbnail, // ✅ Use actual thumbnail path from database
```
## 🔧 Files Fixed
### 1. Cluster Folders API - Fixed Thumbnail Path
**File**: `/src/app/api/clusters/[id]/folders/route.ts`
- **Change**: Use `mediaItem.thumbnail` instead of generating URLs
- **Impact**: Thumbnails now use correct paths like `/api/thumbnails/ff/01/...320.png`
### 2. Cluster Videos API - Added Missing Metadata
**File**: `/src/app/api/clusters/[id]/videos/route.ts`
- **Added**: LEFT JOIN for bookmarks and stars data
- **Impact**: Now includes bookmark_count, avg_rating, star_count (like regular videos API)
### 3. Cluster Photos API - Added Missing Metadata
**File**: `/src/app/api/clusters/[id]/photos/route.ts`
- **Added**: LEFT JOIN for bookmarks and stars data
- **Impact**: Consistent with other APIs
### 4. Cluster Texts API - Added Missing Metadata
**File**: `/src/app/api/clusters/[id]/texts/route.ts`
- **Added**: LEFT JOIN for bookmarks and stars data
- **Impact**: Consistent with other APIs
## ✅ How It Works Now
### Thumbnail System Architecture
```
Database: media.thumbnail = "/api/thumbnails/ff/01/xyz_320.png"
Cluster Folders API: Uses actual thumbnail path from DB
Frontend: <img src="/api/thumbnails/ff/01/xyz_320.png" />
Thumbnail API: /api/thumbnails/[...path]/route.ts serves the image
```
### Before vs After Comparison
| API Endpoint | Before | After |
|--------------|--------|-------|
| **Cluster Folders** | Generated fake URLs | ✅ Uses DB thumbnail paths |
| **Cluster Videos** | Missing metadata | ✅ Includes bookmarks/ratings |
| **Cluster Photos** | Missing metadata | ✅ Includes bookmarks/ratings |
| **Cluster Texts** | Missing metadata | ✅ Includes bookmarks/ratings |
## 🧪 Expected Results
- ✅ Video thumbnails display correctly in cluster folder view
- ✅ Photo thumbnails display correctly in cluster folder view
- ✅ Ratings and bookmarks work in all cluster media views
- ✅ No more 404 errors in browser console
- ✅ Consistent behavior between regular and cluster views
## 📊 Test Status
- [x] TypeScript compilation successful
- [x] No lint errors
- [ ] Manual testing required
- [ ] Browser console verification needed
---
**Status**: ✅ **READY FOR TESTING**
**Fix Applied**: 2025-10-12
**Files Changed**: 4

View File

@ -205,11 +205,7 @@ function handleFolderContents(
id: mediaItem.id,
type: mediaItem.type,
size: mediaItem.size,
thumbnail: mediaItem.type === 'video'
? `/api/videos/${mediaItem.id}/thumbnail`
: mediaItem.type === 'photo'
? `/api/photos/${mediaItem.id}/thumbnail`
: '',
thumbnail: mediaItem.thumbnail, // Use actual thumbnail path from database
avg_rating: mediaItem.avg_rating || 0,
star_count: mediaItem.star_count || 0,
bookmark_count: mediaItem.bookmark_count || 0,

View File

@ -30,10 +30,26 @@ export async function GET(
}
let query = `
SELECT m.*, l.path as library_path
SELECT m.*, l.path as library_path,
COALESCE(b.bookmark_count, 0) as bookmark_count,
COALESCE(s.avg_rating, 0) as avg_rating,
COALESCE(s.star_count, 0) as star_count
FROM media m
INNER JOIN libraries l ON m.library_id = l.id
INNER JOIN library_cluster_mapping lcm ON l.id = lcm.library_id
LEFT JOIN (
SELECT media_id, COUNT(*) as bookmark_count
FROM bookmarks
GROUP BY media_id
) b ON m.id = b.media_id
LEFT JOIN (
SELECT
media_id,
AVG(rating) as avg_rating,
COUNT(*) as star_count
FROM stars
GROUP BY media_id
) s ON m.id = s.media_id
WHERE lcm.cluster_id = ? AND m.type = 'photo'
`;

View File

@ -30,10 +30,26 @@ export async function GET(
}
let query = `
SELECT m.*, l.path as library_path
SELECT m.*, l.path as library_path,
COALESCE(b.bookmark_count, 0) as bookmark_count,
COALESCE(s.avg_rating, 0) as avg_rating,
COALESCE(s.star_count, 0) as star_count
FROM media m
INNER JOIN libraries l ON m.library_id = l.id
INNER JOIN library_cluster_mapping lcm ON l.id = lcm.library_id
LEFT JOIN (
SELECT media_id, COUNT(*) as bookmark_count
FROM bookmarks
GROUP BY media_id
) b ON m.id = b.media_id
LEFT JOIN (
SELECT
media_id,
AVG(rating) as avg_rating,
COUNT(*) as star_count
FROM stars
GROUP BY media_id
) s ON m.id = s.media_id
WHERE lcm.cluster_id = ? AND m.type = 'text'
`;

View File

@ -30,10 +30,26 @@ export async function GET(
}
let query = `
SELECT m.*, l.path as library_path
SELECT m.*, l.path as library_path,
COALESCE(b.bookmark_count, 0) as bookmark_count,
COALESCE(s.avg_rating, 0) as avg_rating,
COALESCE(s.star_count, 0) as star_count
FROM media m
INNER JOIN libraries l ON m.library_id = l.id
INNER JOIN library_cluster_mapping lcm ON l.id = lcm.library_id
LEFT JOIN (
SELECT media_id, COUNT(*) as bookmark_count
FROM bookmarks
GROUP BY media_id
) b ON m.id = b.media_id
LEFT JOIN (
SELECT
media_id,
AVG(rating) as avg_rating,
COUNT(*) as star_count
FROM stars
GROUP BY media_id
) s ON m.id = s.media_id
WHERE lcm.cluster_id = ? AND m.type = 'video'
`;