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:
parent
ce61ebbf9e
commit
335f47549a
|
|
@ -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
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
`;
|
||||
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
`;
|
||||
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
`;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue