# Surprise Me - Video Recommendation Feature Design ## Overview The "Surprise Me" feature is a lightweight video recommendation system designed for personal video management. Unlike enterprise systems like YouTube, this focuses on **rediscovery and variety** for a single user's personal collection, helping users find interesting videos they might have forgotten about or haven't watched recently. ## Design Philosophy ### Core Principles 1. **Discovery Over Prediction**: Help users rediscover their own collection rather than predict preferences 2. **Lightweight**: No machine learning infrastructure required 3. **Variety-Focused**: Ensure diverse recommendations across different libraries/folders 4. **Personal**: Leverage user's own bookmarks, ratings, and viewing patterns 5. **Serendipity**: Balance between smart recommendations and pleasant surprises ### Key Difference from YouTube-Style Recommendations - **No collaborative filtering**: Single user system - **No external data**: Works entirely with user's personal library - **Focus on forgotten content**: Help surface older or unwatched videos - **Simplicity**: Easy to understand and implement ## Architecture ### UI Integration #### Sidebar Navigation ``` ├── Home ├── Settings ├── Videos ├── Photos ├── Texts ├── Bookmarks ├── 🎲 Surprise Me ← NEW ITEM ├── Clusters │ ├── Cluster 1 │ └── Cluster 2 └── Folder Viewer ├── Library 1 └── Library 2 ``` **Important**: "Surprise Me" is a **standalone sidebar item**, NOT a tab within the Videos page. #### Page Layout (`/surprise-me`) When users click "Surprise Me" in the sidebar, they navigate to a dedicated page showing: ``` ┌─────────────────────────────────────────────────────────┐ │ 🎲 Surprise Me │ │ │ │ Discover videos from your collection │ │ │ │ [🔄 Refresh Recommendations] [⚙️ Algorithm: Smart Mix]│ └─────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────┐ │ Recommended for You │ │ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │ │ │Video │ │Video │ │Video │ │Video │ │Video │ │ │ │ #1 │ │ #2 │ │ #3 │ │ #4 │ │ #5 │ │ │ └──────┘ └──────┘ └──────┘ └──────┘ └──────┘ │ │ │ │ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │ │ │Video │ │Video │ │Video │ │Video │ │Video │ │ │ │ #6 │ │ #7 │ │ #8 │ │ #9 │ │ #10 │ │ │ └──────┘ └──────┘ └──────┘ └──────┘ └──────┘ │ └─────────────────────────────────────────────────────────┘ ``` ### Database Schema Extensions #### New Table: `media_access` Tracks user interactions with media for better recommendations. ```sql CREATE TABLE media_access ( id INTEGER PRIMARY KEY AUTOINCREMENT, media_id INTEGER NOT NULL, access_type TEXT NOT NULL CHECK (access_type IN ('view', 'play', 'bookmark', 'rate')), created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (media_id) REFERENCES media(id) ON DELETE CASCADE ); -- Indexes for performance CREATE INDEX idx_media_access_media_id ON media_access(media_id); CREATE INDEX idx_media_access_created_at ON media_access(created_at); CREATE INDEX idx_media_access_type_created_at ON media_access(access_type, created_at); ``` **Usage**: - `view`: When video modal/player is opened - `play`: When video playback actually starts - `bookmark`: When video is bookmarked - `rate`: When video is rated ### API Endpoints #### GET `/api/videos/recommendations` **Query Parameters**: - `algorithm` (optional): Recommendation algorithm to use - `smart_mix` (default): Balanced mix of all algorithms - `weighted_random`: Smart randomization with recency weighting - `forgotten_gems`: Bookmarked/rated but not recently viewed - `similar_to_favorites`: From same folders as highly-rated videos - `time_based`: Based on video creation/addition time - `pure_random`: Complete randomization - `unwatched_first`: Prioritize never-accessed videos - `limit` (optional, default: 20): Number of recommendations - `exclude_recent_days` (optional, default: 30): Exclude videos viewed in last N days **Response**: ```json { "recommendations": [ { "id": 123, "title": "Movie Title", "path": "/mnt/videos/movie.mp4", "size": 1073741824, "thumbnail": "/api/thumbnails/...", "type": "video", "bookmark_count": 0, "star_count": 1, "avg_rating": 4.5, "created_at": "2024-01-15T10:30:00Z", "recommendation_reason": "From your favorite library", "recommendation_score": 0.85 } ], "algorithm": "smart_mix", "total_videos": 1500, "total_recommended": 20 } ``` #### POST `/api/media-access/:id` Track media access for recommendation improvement. **Body**: ```json { "accessType": "view" | "play" | "bookmark" | "rate" } ``` **Response**: ```json { "success": true, "mediaId": 123 } ``` ## Recommendation Algorithms ### 1. Smart Mix (Default) Combines multiple strategies for balanced recommendations: - 30% Weighted Random Discovery - 25% Forgotten Gems - 20% Similar to Favorites - 15% Unwatched First - 10% Time-Based Variety **Implementation**: ```typescript async function getSmartMixRecommendations(limit: number) { const recommendations = []; // Calculate distribution const distribution = { weightedRandom: Math.floor(limit * 0.3), forgottenGems: Math.floor(limit * 0.25), similarToFavorites: Math.floor(limit * 0.2), unwatchedFirst: Math.floor(limit * 0.15), timeBased: Math.floor(limit * 0.1) }; // Get from each algorithm recommendations.push(...await getWeightedRandom(distribution.weightedRandom)); recommendations.push(...await getForgottenGems(distribution.forgottenGems)); recommendations.push(...await getSimilarToFavorites(distribution.similarToFavorites)); recommendations.push(...await getUnwatchedFirst(distribution.unwatchedFirst)); recommendations.push(...await getTimeBased(distribution.timeBased)); // Shuffle and deduplicate return shuffleAndDeduplicate(recommendations).slice(0, limit); } ``` ### 2. Weighted Random Discovery Smart randomization that considers: - **Recency penalty**: Videos viewed recently get lower weight - **Rating boost**: Higher-rated videos get slight preference - **Library diversity**: Ensure videos from different sources **Scoring Formula**: ``` score = base_score × recency_multiplier × rating_multiplier × diversity_bonus where: base_score = random(0.5, 1.0) recency_multiplier = min(1.0, days_since_last_view / 90) rating_multiplier = 1.0 + (avg_rating / 10) diversity_bonus = 1.2 if from underrepresented library ``` **SQL Query**: ```sql SELECT m.*, RANDOM() as random_factor, JULIANDAY('now') - JULIANDAY(COALESCE(ma.last_access, m.created_at)) as days_since_view, COALESCE(m.avg_rating, 0) as rating FROM media m LEFT JOIN ( SELECT media_id, MAX(created_at) as last_access FROM media_access WHERE access_type IN ('view', 'play') GROUP BY media_id ) ma ON m.id = ma.media_id WHERE m.type = 'video' AND (ma.last_access IS NULL OR ma.last_access < datetime('now', '-30 days')) ORDER BY ( random_factor * MIN(1.0, (JULIANDAY('now') - JULIANDAY(COALESCE(ma.last_access, m.created_at))) / 90.0) * (1.0 + COALESCE(m.avg_rating, 0) / 10.0) ) DESC LIMIT ? ``` ### 3. Forgotten Gems Surface videos that you've shown interest in but haven't watched recently: - Bookmarked videos not viewed in 30+ days - Highly-rated videos (4+ stars) not viewed in 60+ days - Videos from folders you've bookmarked **Target Criteria**: - Has bookmark OR rating >= 4 - Not accessed in last 30-60 days - Sorted by rating and bookmark combination **SQL Query**: ```sql SELECT DISTINCT m.*, CASE WHEN b.id IS NOT NULL THEN 'You bookmarked this' WHEN m.avg_rating >= 4 THEN 'You rated this highly' ELSE 'From a favorite folder' END as recommendation_reason FROM media m LEFT JOIN bookmarks b ON m.id = b.media_id LEFT JOIN ( SELECT media_id, MAX(created_at) as last_access FROM media_access WHERE access_type IN ('view', 'play') GROUP BY media_id ) ma ON m.id = ma.media_id WHERE m.type = 'video' AND ( b.id IS NOT NULL OR m.avg_rating >= 4 OR m.path LIKE (SELECT folder_path || '%' FROM folder_bookmarks LIMIT 1) ) AND (ma.last_access IS NULL OR ma.last_access < datetime('now', '-30 days')) ORDER BY (COALESCE(m.avg_rating, 0) * 0.7 + CASE WHEN b.id IS NOT NULL THEN 3 ELSE 0 END) DESC, RANDOM() LIMIT ? ``` ### 4. Similar to Favorites Find videos from the same location as your highly-rated content: - Same parent folder as 4+ star videos - Same library as frequently accessed content - Similar file size/characteristics **Logic**: 1. Find user's top-rated videos (4+ stars) 2. Extract their parent folders 3. Find other videos in those folders 4. Exclude already-watched videos **SQL Query**: ```sql WITH favorite_folders AS ( SELECT DISTINCT SUBSTR(path, 1, LENGTH(path) - LENGTH(SUBSTR(path, INSTR(path, '/', -1) + 1))) as folder_path FROM media WHERE type = 'video' AND avg_rating >= 4 LIMIT 10 ) SELECT m.*, 'From a folder with your favorites' as recommendation_reason FROM media m LEFT JOIN media_access ma ON m.id = ma.media_id AND ma.access_type IN ('view', 'play') WHERE m.type = 'video' AND EXISTS ( SELECT 1 FROM favorite_folders ff WHERE m.path LIKE ff.folder_path || '%' ) AND m.avg_rating < 4 -- Not already a favorite AND (ma.id IS NULL OR ma.created_at < datetime('now', '-60 days')) ORDER BY RANDOM() LIMIT ? ``` ### 5. Time-Based Recommendations Recommendations based on temporal patterns: - **Seasonal**: Videos added around the same time last year - **New discoveries**: Recently added to library but not yet viewed - **Old but gold**: Videos added long ago that you might have missed **Categories**: ```typescript type TimeBasedCategory = | 'recently_added' // Added in last 7 days, not viewed | 'one_month_old' // Added ~30 days ago | 'seasonal' // Added ~365 days ago | 'archive_discovery' // Added >1 year ago, never viewed ``` ### 6. Pure Random Complete randomization with minimal filters: - Only filter: video type and exclude corrupted files - Pure serendipity mode - No bias towards ratings or access patterns ### 7. Unwatched First Prioritize videos that have never been accessed: - Never appeared in `media_access` table - Sorted by date added (newest first) - Ensures new content gets discovered **SQL Query**: ```sql SELECT m.*, 'Never watched before' as recommendation_reason FROM media m LEFT JOIN media_access ma ON m.id = ma.media_id WHERE m.type = 'video' AND ma.id IS NULL ORDER BY m.created_at DESC LIMIT ? ``` ## Implementation Plan ### Phase 1: Foundation (Priority: P0) - [x] Design documentation - [ ] Database schema migration (add `media_access` table) - [ ] Media access tracking utility functions - [ ] Basic recommendation service structure ### Phase 2: Core Algorithms (Priority: P0) - [ ] Implement Weighted Random algorithm - [ ] Implement Unwatched First algorithm - [ ] Implement Pure Random algorithm - [ ] Create API endpoint `/api/videos/recommendations` - [ ] Create media access tracking endpoint ### Phase 3: Advanced Algorithms (Priority: P1) - [ ] Implement Forgotten Gems algorithm - [ ] Implement Similar to Favorites algorithm - [ ] Implement Time-Based algorithm - [ ] Implement Smart Mix algorithm ### Phase 4: UI Components (Priority: P0) - [ ] Create `SurpriseMePage` component (`/src/app/surprise-me/page.tsx`) - [ ] Add "Surprise Me" item to sidebar navigation - [ ] Implement recommendation grid display - [ ] Add algorithm selector dropdown - [ ] Add refresh button functionality ### Phase 5: Integration & Polish (Priority: P1) - [ ] Track video views automatically when player opens - [ ] Add recommendation reasons/badges to video cards - [ ] Implement smooth transitions and loading states - [ ] Add empty state when no recommendations available - [ ] Performance testing with large libraries ### Phase 6: Advanced Features (Priority: P2) - [ ] Save preferred algorithm in user preferences - [ ] "Not interested" button to exclude videos - [ ] Recommendation history tracking - [ ] Export/share recommendations - [ ] Statistics dashboard (most recommended, never recommended, etc.) ## File Structure ``` /root/workspace/nextav/ ├── src/ │ ├── app/ │ │ ├── api/ │ │ │ ├── media-access/ │ │ │ │ └── [id]/ │ │ │ │ └── route.ts ← NEW: Track media access │ │ │ └── videos/ │ │ │ └── recommendations/ │ │ │ └── route.ts ← NEW: Get recommendations │ │ └── surprise-me/ │ │ └── page.tsx ← NEW: Surprise Me page │ ├── components/ │ │ ├── recommendation-grid.tsx ← NEW: Display recommendations │ │ └── sidebar.tsx ← MODIFY: Add Surprise Me item │ ├── db/ │ │ └── index.ts ← MODIFY: Add media_access table │ └── lib/ │ └── recommendation-service.ts ← NEW: Recommendation algorithms └── docs/ └── SURPRISE_ME_RECOMMENDATION_DESIGN.md ← This file ``` ## User Experience Flow ### Scenario 1: First Time User 1. User clicks "🎲 Surprise Me" in sidebar 2. Page shows loading state 3. Default "Smart Mix" algorithm runs 4. 20 diverse videos displayed 5. User can click any video to watch 6. User can click "Refresh" for new recommendations ### Scenario 2: Regular User 1. User navigates to Surprise Me 2. System recognizes viewing patterns 3. Shows mix of: - Unwatched videos from favorite libraries - Old favorites not seen recently - Random discoveries for variety 4. Each card shows reason: "From your favorite library" or "Never watched" ### Scenario 3: Algorithm Exploration 1. User selects "Forgotten Gems" from dropdown 2. System shows only bookmarked/highly-rated unwatched content 3. User finds an old favorite they forgot about 4. User watches and enjoys rediscovery ## Performance Considerations ### Database Optimization - All queries use indexed columns (`media_id`, `created_at`, `type`) - Limit queries to reasonable batch sizes (20-50 videos) - Use `RANDOM()` efficiently with proper `ORDER BY` - Cache recommendation results for 5-10 minutes ### Caching Strategy ```typescript // Cache recommendations for short period const CACHE_TTL = 5 * 60 * 1000; // 5 minutes const recommendationCache = new Map(); ``` ### Scalability - Works efficiently with 1,000 to 100,000+ videos - Lazy loading for recommendation display - Pagination for large result sets - Background precomputation for complex algorithms (future enhancement) ## Configuration Options ### User Preferences (Future) ```typescript interface RecommendationPreferences { defaultAlgorithm: RecommendationAlgorithm; excludeRecentDays: number; // Default: 30 recommendationLimit: number; // Default: 20 enableAutoRefresh: boolean; // Auto-refresh on page visit preferredLibraries: number[]; // Boost these libraries excludedFolders: string[]; // Never recommend from these } ``` ### System Settings ```typescript const RECOMMENDATION_CONFIG = { MAX_RECOMMENDATIONS: 100, DEFAULT_LIMIT: 20, MIN_RATING_FOR_FAVORITES: 4, RECENT_VIEW_THRESHOLD_DAYS: 30, DIVERSITY_BOOST_FACTOR: 1.2, CACHE_TTL_SECONDS: 300 }; ``` ## Testing Strategy ### Unit Tests - Test each algorithm independently - Verify SQL query correctness - Test edge cases (empty library, all videos watched, etc.) ### Integration Tests - Test API endpoint responses - Verify media access tracking - Test algorithm switching ### User Acceptance Testing - Test with small library (<100 videos) - Test with medium library (100-1000 videos) - Test with large library (>10,000 videos) - Verify recommendations are diverse and interesting ## Future Enhancements ### Phase 2 Features (Post-MVP) 1. **Playlist Generation**: Create playlists from recommendations 2. **Mood-Based**: Filter by video characteristics (length, genre from filename) 3. **Social**: Share recommendation sets with other users 4. **Learning**: Track which recommendations user clicks to improve algorithms 5. **Filters**: By library, rating range, size, date added 6. **Smart Scheduling**: "Video of the Day" feature 7. **Collections**: "Similar to this video" on video player 8. **Statistics**: Dashboard showing recommendation effectiveness ### Advanced Algorithms (Future) 1. **Content-Based Filtering**: Use filename patterns, folder structure 2. **Collaborative Patterns**: If multiple users, find correlation 3. **Temporal Patterns**: Learn user's viewing time preferences 4. **Sequence Learning**: Detect video series/collections 5. **Quality Scoring**: Prefer higher quality encodes ## Success Metrics ### Key Performance Indicators - **Engagement**: % of recommendations clicked - **Discovery Rate**: # of previously unwatched videos discovered - **Diversity Score**: # of different libraries in recommendations - **User Satisfaction**: Time spent on Surprise Me page - **Rediscovery**: % of forgotten favorites surfaced ### Target Goals - 80%+ of users try Surprise Me feature - 30%+ click-through rate on recommendations - 50%+ of recommendations are from unwatched videos - 70%+ user satisfaction with variety ## Appendix ### Recommendation Reason Badges Visual indicators showing why a video was recommended: | Badge | Meaning | Color | |-------|---------|-------| | 🌟 Favorite | From folder with 4+ star videos | Gold | | 🔖 Bookmarked | You bookmarked this | Blue | | ✨ New | Added recently, never watched | Green | | 🕰️ Forgotten Gem | Rated highly but not watched in 60+ days | Purple | | 🎲 Random | Pure random selection | Gray | | 📁 Same Folder | From folder with your favorites | Orange | | 🎯 Unwatched | Never accessed before | Teal | ### Example UI Component Props ```typescript interface SurpriseMePageProps { initialAlgorithm?: RecommendationAlgorithm; initialLimit?: number; } interface RecommendationCardProps { video: Video; reason: string; score: number; onVideoClick: (video: Video) => void; } ``` --- **Document Version**: 1.0 **Last Updated**: 2025-10-12 **Status**: Design Phase **Next Steps**: Begin Phase 1 implementation