Fix video library thumbnail spacing and create media cleanup plan
FIXED: Video library thumbnail spacing issues - Identified multiple conflicting video gallery systems in index.html - Applied proper CSS fixes to #lib-video-gallery (gap: 4px, height: 240px) - Resolved CSS conflicts between unified-video-gallery and lib-video-gallery - Enhanced video info display visibility and styling - Successfully created compact layout with visible video titles CREATED: Comprehensive media library cleanup plan - Documented 3 duplicate video library systems requiring consolidation - Identified duplicate image and audio gallery systems - Created phased cleanup approach in docs/MEDIA_LIBRARY_CLEANUP_PLAN.md - Estimated ~1,150 lines of duplicate code to be removed - Preserved all functionality while simplifying architecture RESULT: Working video library with proper spacing and video info display - lib-video-gallery confirmed as primary system to keep - All other video gallery systems marked for removal - Next: Execute cleanup phases to eliminate duplicates
This commit is contained in:
parent
5983d4a3bb
commit
b5ef5d2c77
33
README.md
33
README.md
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
*How long can you last?*
|
||||
|
||||
An adult edging challenge game with multiple specialized modes, progressive training systems, and customizable punishment mechanics.
|
||||
An adult edging challenge game with multiple specialized modes, professional media player system, and customizable punishment mechanics.
|
||||
|
||||
## 🎮 Game Modes
|
||||
|
||||
- **Quick Play** - Fast-start mode with background videos and streamlined interface
|
||||
- **Standard Game** - Classic endless task mode
|
||||
- **Quick Play** - Fast-start mode with background videos, task disable toggles, and streamlined interface
|
||||
- **Standard Game** - Classic endless task mode with full feature set
|
||||
- **Timed Challenge** - Race against the clock
|
||||
- **Score Target** - Reach target points to win
|
||||
- **Training Academy** - Gooning-focused training with progressive scenarios
|
||||
|
|
@ -15,22 +15,28 @@ An adult edging challenge game with multiple specialized modes, progressive trai
|
|||
- **Endurance Trials** - Progressive endurance training (30s to 10min sessions)
|
||||
- **Photography Studio** - Webcam photography and posing sessions
|
||||
|
||||
## <EFBFBD> Core Features
|
||||
## ⭐ Core Features
|
||||
|
||||
### 🎬 Professional Media Player System
|
||||
- **Quick Play**: Fast-start mode with background videos, compact controls, and streamlined gameplay
|
||||
- **Quick Play**: Fast-start mode with background videos, task management with disable toggles, and session cleanup
|
||||
- **Porn Cinema**: Full-featured video player with playlists, seeking, and theater mode
|
||||
- **Multi-Screen Quad Player**: 4-video grid layout for intensive training sessions
|
||||
- **Video Library Management**: Unified access to linked video directories
|
||||
- **Video Library Management**: Unified access to linked video directories with 40+ videos
|
||||
- **Background Video Integration**: Seamless video playback during tasks with opacity controls
|
||||
- **Focus Interruptions**: Video overlays during training scenarios
|
||||
- **Player Statistics**: Watch time tracking, completion rates, and viewing analytics
|
||||
|
||||
### Progressive Endurance Training
|
||||
- **Experience Levels**: Beginner → Intermediate → Advanced → Expert
|
||||
### 🎮 Task Management System
|
||||
- **Task Disable Toggles**: Individual task control with persistent settings in Quick Play mode
|
||||
- **Real-time Filtering**: Disabled tasks excluded from gameplay with ID consistency
|
||||
- **Consequence Integration**: Disabled tasks also filtered from consequence system
|
||||
- **Debug Logging**: Comprehensive console output for troubleshooting
|
||||
|
||||
### 📊 Progressive Systems
|
||||
- **Experience Levels**: Beginner → Intermediate → Advanced → Expert progression
|
||||
- **Adaptive Timers**: 30 seconds to 10 minutes based on skill level
|
||||
- **Certification System**: Graduate through progressive difficulty
|
||||
- **Timer-Based Focus**: Performance tasks without minigames
|
||||
- **Certification System**: Graduate through progressive difficulty tiers
|
||||
- **Player Statistics**: Comprehensive tracking across all game modes
|
||||
|
||||
### Interactive Task System
|
||||
- **Mirror Tasks**: Webcam-based self-humiliation challenges
|
||||
|
|
@ -111,6 +117,13 @@ webGame/
|
|||
|
||||
## 📋 Recent Updates
|
||||
|
||||
### v3.2 - Task Management & Session Cleanup (November 6, 2025)
|
||||
- ✅ **Task Disable Toggles**: Individual task control with persistent localStorage settings
|
||||
- ✅ **Consequence System Integration**: Disabled tasks filtered from consequence selection
|
||||
- ✅ **ID Consistency**: Fixed task ID generation to handle both numeric and generated IDs
|
||||
- ✅ **Session Cleanup**: Comprehensive endGame cleanup stopping videos and periodic popups
|
||||
- ✅ **Debug System**: Extensive console logging for task state tracking and troubleshooting
|
||||
|
||||
### v3.1 - Quick Play Background Video System (November 3, 2025)
|
||||
- ✅ **Quick Play Mode**: New fast-start game mode with streamlined interface
|
||||
- ✅ **Background Video Integration**: Independent video system with 46+ videos detected
|
||||
|
|
|
|||
184
ROADMAP.md
184
ROADMAP.md
|
|
@ -1,184 +0,0 @@
|
|||
# Gooner Training Academy - Development Roadmap
|
||||
|
||||
## 🎮 Current Status
|
||||
- ✅ Core game mechanics implemented
|
||||
- ✅ Interactive task system with focus interruptions
|
||||
- ✅ Video integration for focus sessions
|
||||
- ✅ Audio and image management
|
||||
- ✅ Scenario system with state management
|
||||
- ✅ Loading overlay and initialization protection
|
||||
- ✅ Desktop file management integration
|
||||
- ✅ Continuous audio playlist system
|
||||
- ✅ XP-based progression system (replaced scoring)
|
||||
- ✅ **Counter System Removal (October 2025)**
|
||||
- Removed arousal/control/intensity counter mechanics
|
||||
- Cleaned up all related UI components and processing logic
|
||||
- Simplified game flow without statistical tracking overhead
|
||||
- ✅ **🎬 Porn Cinema Major Milestone (October 30-31, 2025)**
|
||||
- Complete professional media player interface implemented
|
||||
- Modern layout with header navigation and sidebar panels
|
||||
- Interactive video controls with progress bar seeking
|
||||
- Auto-hide control behavior and responsive design
|
||||
- Video library, playlist management, and search functionality
|
||||
- Full video loading system with unified library integration
|
||||
|
||||
## 🚧 Active Development
|
||||
- Enhanced user experience improvements
|
||||
- Bug fixes and stability enhancements
|
||||
- Performance optimizations
|
||||
- **<EFBFBD> Quick Play Background Video System** *(✅ COMPLETED - November 3, 2025)*
|
||||
- ✅ **Independent Background Videos**: Complete video system with 46+ videos detected from linked directories
|
||||
- ✅ **Compact Video Controls**: Floating controls with opacity cycling (hidden/dim/normal/bright) and visibility toggle
|
||||
- ✅ **Game Mode Isolation**: Proper Quick Play mode preventing main game interference with independent task completion
|
||||
- ✅ **Real Task Integration**: Full integration with 17 mainGameData tasks and 60 task images from linked directories
|
||||
- ✅ **Enhanced Timer System**: Fixed countdown functionality with proper Quick Play state management
|
||||
- ✅ **VideoLibrary Scanning**: Custom video detection system replicating VideoLibrary functionality independently
|
||||
- **🎬 Multi-Screen Quad Player** *(✅ COMPLETED - October 31, 2025)*
|
||||
- ✅ **Quad Video Layout**: 2x2 grid displaying 4 simultaneous video streams for intensive training
|
||||
- ✅ **Training Integration**: Multi-Screen Mode button added to interactive task controls
|
||||
- ✅ **Smart Controls**: Shuffle all videos, minimize to background, and close functionality
|
||||
- ✅ **Minimize Feature**: Continue videos in background while progressing through training scenarios
|
||||
- ✅ **Proper Cleanup**: Videos stop when game ends, DOM corruption prevention with force recreation
|
||||
- **🎬 Porn Cinema Media Player** *(✅ COMPLETED - October 30-31, 2025)*
|
||||
- ✅ **Complete Professional Interface**: Modern two-column layout with header navigation and sidebar panels
|
||||
- ✅ **Full Video Player Controls**: Interactive progress bar, auto-hide behavior, quality/speed selection, volume control
|
||||
- ✅ **Playlist & Library System**: Video library integration, playlist management, search functionality
|
||||
- ✅ **Responsive Design**: Clean architecture with purple gradient theming
|
||||
- ✅ **Video Loading System**: Robust video loading from unified video library with fallback mechanisms
|
||||
- **🔧 Base Video Player Architecture** *(✅ COMPLETED - October 31, 2025)*
|
||||
- ✅ **Reusable Components**: Created BaseVideoPlayer class with full video control functionality (400+ lines)
|
||||
- ✅ **Focus Video Player**: Built FocusVideoPlayer extending BaseVideoPlayer for minimal focus session UI
|
||||
- ✅ **Shared CSS System**: Created base-video-player.css for reusable video styling across game modes
|
||||
- ✅ **Integration & Testing**: Successfully integrated with focus interruptions and validated with comprehensive tests
|
||||
- **🎬 Porn Cinema Refactoring** *(✅ COMPLETED - October 31, 2025)*
|
||||
- ✅ **Clean Architecture**: Created PornCinema class extending BaseVideoPlayer for shared functionality
|
||||
- ✅ **Legacy Preservation**: Created pornCinema-backup.js to preserve original implementation
|
||||
- ✅ **Integration**: Proper inheritance with initialize(), playVideo(), addToPlaylist() methods
|
||||
- **🎮 Focus Interruption Video Integration** *(✅ COMPLETED - October 31, 2025)*
|
||||
- ✅ **Video Library Access**: Fixed FocusVideoPlayer video library initialization with 34 videos integrated
|
||||
- ✅ **Ultra-Compact Controls**: Added 60px width volume slider with CSS override system
|
||||
- ✅ **Complete Integration**: Focus interruption videos playing successfully with proper controls
|
||||
- **✅ XP System Implementation (October 2025)**
|
||||
- **Quick-Play**: 1 XP for tasks completed under 5 minutes, 2 XP for tasks 5-10 minutes, 3 XP for tasks over 10 minutes
|
||||
- **Scenario Games**: Time-based XP (1 XP per 2 minutes), webcam bonuses(x2 Multiplier per minute), photo rewards 2 XP per picture
|
||||
- **Porn Cinema**: 1 XP per 5 minutes of video watched
|
||||
|
||||
## 📋 Feature Backlog
|
||||
|
||||
### 🎯 High Priority (Core Game Polish)
|
||||
- [✅] **Quick Play Background Video System** - *Completed November 3, 2025*
|
||||
- **✅ Fast-Start Game Mode**: Streamlined interface with background video integration for immediate gameplay
|
||||
- **✅ Independent Video System**: 46+ videos detected with custom VideoLibrary scanning logic
|
||||
- **✅ Compact UI Controls**: Floating video controls with opacity cycling and quick visibility toggle
|
||||
- **✅ Real Task Integration**: Full integration with mainGameData tasks and linked image directories
|
||||
- **✅ Isolated Game Logic**: Proper mode separation preventing main game interference with dedicated completion functions
|
||||
- [✅] **Porn Cinema Media Player** - *Completed October 30-31, 2025*
|
||||
- **✅ Complete Professional Interface**: Two-column responsive layout, header navigation, tabbed sidebar
|
||||
- **✅ Full Video Player**: Interactive progress bar, auto-hide controls, quality/speed selection, volume control
|
||||
- **✅ Advanced Features**: Video library integration, playlist management, search functionality, theater mode
|
||||
- **✅ User Experience**: Hover effects, keyboard shortcuts support, responsive design, purple gradient theming
|
||||
- [ ] More interactive task types and scenarios
|
||||
- [ ] Improved user interface and visual design
|
||||
- [ ] Better error handling and user feedback
|
||||
- [ ] Performance optimizations for large media libraries
|
||||
- [ ] Comprehensive testing and bug fixes
|
||||
- [ ] User settings and customization options
|
||||
- [ ] Save/load game state improvements
|
||||
|
||||
### 🎨 Medium Priority (User Experience)
|
||||
- [ ] Advanced scenario editor
|
||||
- [ ] Custom task creation tools
|
||||
- [ ] Enhanced statistics and progress tracking
|
||||
- [ ] Multiple game mode variations
|
||||
- [ ] Improved audio/visual feedback systems
|
||||
- [ ] Mobile-responsive design considerations
|
||||
- [ ] Accessibility features
|
||||
|
||||
### 🎵 Content & Media
|
||||
- [ ] Built-in content library (sample images/audio/video)
|
||||
- [ ] Content recommendation system
|
||||
- [ ] Media organization and tagging features
|
||||
- [ ] Import/export functionality for user content
|
||||
- [ ] Content validation and quality checks
|
||||
- [ ] **Custom Audio Playlist Creation** - Allow users to create and save custom audio playlists with:
|
||||
- Drag-and-drop playlist editor
|
||||
- Named playlists (e.g., "Intense Session", "Relaxing Background")
|
||||
- Shuffle/repeat options per playlist
|
||||
- Playlist sharing and import/export
|
||||
- Time-based playlist scheduling (different playlists for different game phases)
|
||||
- Crossfade and transition effects between tracks
|
||||
|
||||
### 🔧 Technical Improvements
|
||||
- [ ] Code refactoring and optimization
|
||||
- [ ] Automated testing suite
|
||||
- [ ] Better documentation and code comments
|
||||
- [ ] Cross-platform compatibility testing
|
||||
- [ ] Security enhancements
|
||||
- [ ] Backup and recovery features
|
||||
|
||||
### 🔮 Future Enhancements (Low Priority)
|
||||
- [ ] Enhanced AI task generation features
|
||||
- *Note: NSFW-friendly AI models are limited and expensive. Manual content creation is more reliable for now.*
|
||||
- [ ] Advanced machine learning features
|
||||
- [ ] Voice recognition/synthesis integration
|
||||
- [ ] VR/AR compatibility exploration
|
||||
|
||||
## 💰 Future Monetization Considerations
|
||||
*Added: October 29, 2025*
|
||||
|
||||
When the core game is feature-complete and polished, consider:
|
||||
|
||||
### Freemium Model Concepts
|
||||
- **Free Tier**: Basic game modes, limited media (50 images, 20 audio files, 10 videos)
|
||||
- **Premium Tier**: Unlimited media, all scenarios, advanced features
|
||||
- **Subscription vs One-time**: Consider both models
|
||||
|
||||
### Distribution Platforms
|
||||
- **Itch.io**: Indie-friendly, adult content allowed, easy setup
|
||||
- **Steam**: Large audience, requires more polish and marketing
|
||||
- **Direct Sales**: Own website with payment processing
|
||||
- **Patreon/SubscribeStar**: Subscription-based community building
|
||||
|
||||
### Technical Requirements for Monetization
|
||||
- License management system
|
||||
- Content gating and feature restrictions
|
||||
- Payment integration (Stripe, PayPal)
|
||||
- User account system
|
||||
- Analytics and usage tracking
|
||||
- Anti-piracy measures (lightweight)
|
||||
|
||||
### Marketing Strategy
|
||||
- Demo videos showcasing unique features
|
||||
- Community building (Discord, Reddit)
|
||||
- SEO-optimized website
|
||||
- Content creator partnerships
|
||||
- Social media presence
|
||||
|
||||
**Note**: Focus on creating an exceptional user experience first. A great free product will naturally generate demand for premium features.
|
||||
|
||||
## 🗓️ Version Planning
|
||||
|
||||
### v1.0 - Core Game Release
|
||||
- All essential features working reliably
|
||||
- Comprehensive testing completed
|
||||
- Documentation and user guides
|
||||
- Stable performance with large media libraries
|
||||
|
||||
### v1.5 - Enhanced Experience
|
||||
- Advanced scenarios and customization
|
||||
- Improved UI/UX
|
||||
- Additional game modes
|
||||
|
||||
### v2.0 - Community Features
|
||||
- User-generated content support
|
||||
- Sharing and community features
|
||||
- Advanced customization tools
|
||||
|
||||
### vFuture - Commercial Considerations
|
||||
- Monetization system implementation
|
||||
- Multi-platform distribution
|
||||
- Advanced analytics and user management
|
||||
|
||||
---
|
||||
|
||||
*This roadmap is a living document and will be updated as development progresses.*
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
# 🧹 MEDIA LIBRARY CLEANUP PLAN
|
||||
*Generated: November 8, 2025*
|
||||
|
||||
## 📋 OVERVIEW
|
||||
After extensive debugging of video library thumbnail spacing, we discovered multiple duplicate media library systems throughout `index.html`. This document outlines a comprehensive cleanup plan to consolidate into single, clean media management systems.
|
||||
|
||||
---
|
||||
|
||||
## 🎬 VIDEO LIBRARY SYSTEMS ANALYSIS
|
||||
|
||||
### ✅ **KEEP: Library Video Gallery**
|
||||
- **Location:** Line 1546 (`library-video-content` section)
|
||||
- **Element ID:** `lib-video-gallery`
|
||||
- **Functions:** `populateVideoGallery()` (Line 6067)
|
||||
- **Status:** ✅ **WORKING** - Recently fixed thumbnail spacing and video info display
|
||||
- **Purpose:** Primary media library management interface
|
||||
|
||||
### ❌ **REMOVE: Unified Video Gallery**
|
||||
- **Location:** Line 501 (main game screen)
|
||||
- **Element ID:** `unified-video-gallery`
|
||||
- **Functions:**
|
||||
- `loadUnifiedVideoGallery()` (Line 2290)
|
||||
- `loadStandardVideoGallery()` (Line 2356)
|
||||
- `loadLargeVideoGallery()` (Line 2367)
|
||||
- **Status:** ❌ **DUPLICATE** - Overlaps with lib-video-gallery functionality
|
||||
|
||||
### ❌ **REMOVE: Dynamic Category Galleries**
|
||||
- **Location:** Generated by `loadVideoGalleryContent()` (Line 2663)
|
||||
- **Element IDs:** `${category}-video-gallery` creates:
|
||||
- `background-video-gallery`
|
||||
- `task-video-gallery`
|
||||
- `training-video-gallery`
|
||||
- `reward-video-gallery`
|
||||
- `punishment-video-gallery`
|
||||
- **Status:** ❌ **DUPLICATE** - Category filtering can be handled by lib-video-gallery
|
||||
|
||||
### ❌ **REMOVE: Support Functions**
|
||||
- `setupVideoItemHandlers()` (Line 2507)
|
||||
- Selection/deletion functions (Lines 2901-2991)
|
||||
- **Status:** ❌ **REDUNDANT** - Functionality duplicated in working system
|
||||
|
||||
---
|
||||
|
||||
## 📸 IMAGE LIBRARY SYSTEMS ANALYSIS
|
||||
|
||||
### ✅ **KEEP: Library Image Gallery**
|
||||
- **Location:** Line 1453 (`library-gallery-content` section)
|
||||
- **Element ID:** `lib-image-gallery`
|
||||
- **Functions:** Lines 4361-4395
|
||||
- **Status:** ✅ **WORKING** - Primary image management interface
|
||||
|
||||
### ❌ **REMOVE: Task/Consequence Image Galleries**
|
||||
- **Location:** Lines 310, 330, 334 (main game screen)
|
||||
- **Element IDs:**
|
||||
- `task-images-gallery` (Line 330)
|
||||
- `consequence-images-gallery` (Line 334)
|
||||
- **Status:** ❌ **DUPLICATE** - Can be consolidated into lib-image-gallery
|
||||
|
||||
---
|
||||
|
||||
## 🔊 AUDIO LIBRARY SYSTEMS ANALYSIS
|
||||
|
||||
### ✅ **KEEP: Library Audio Gallery**
|
||||
- **Location:** Line 1500 (`library-audio-content` section)
|
||||
- **Element ID:** `lib-audio-gallery`
|
||||
- **Status:** ✅ **PRIMARY** - Main audio management interface
|
||||
|
||||
### ❌ **REMOVE: Background/Ambient Audio Galleries**
|
||||
- **Location:** Lines 389, 409, 413 (main game screen)
|
||||
- **Element IDs:**
|
||||
- `background-audio-gallery` (Line 409)
|
||||
- `ambient-audio-gallery` (Line 413)
|
||||
- **Status:** ❌ **DUPLICATE** - Can be consolidated with category filtering
|
||||
|
||||
---
|
||||
|
||||
## 🗂️ CLEANUP PHASES
|
||||
|
||||
### **PHASE 1: VIDEO LIBRARY CONSOLIDATION**
|
||||
|
||||
#### Step 1.1: Remove Unified Video Gallery
|
||||
- [ ] Delete `unified-video-gallery` element (Line 501)
|
||||
- [ ] Remove `loadUnifiedVideoGallery()` function (Line 2290)
|
||||
- [ ] Remove `loadStandardVideoGallery()` function (Line 2356)
|
||||
- [ ] Remove `loadLargeVideoGallery()` function (Line 2367)
|
||||
- [ ] Update all references to use `lib-video-gallery`
|
||||
|
||||
#### Step 1.2: Remove Dynamic Category Galleries
|
||||
- [ ] Delete `loadVideoGalleryContent()` function (Line 2663)
|
||||
- [ ] Remove all `${category}-video-gallery` references
|
||||
- [ ] Update category filtering in `lib-video-gallery`
|
||||
|
||||
#### Step 1.3: Clean Support Functions
|
||||
- [ ] Remove `setupVideoItemHandlers()` (Line 2507)
|
||||
- [ ] Remove selection/deletion functions (Lines 2901-2991)
|
||||
- [ ] Integrate necessary functionality into working system
|
||||
|
||||
### **PHASE 2: IMAGE LIBRARY CONSOLIDATION**
|
||||
|
||||
#### Step 2.1: Remove Task/Consequence Galleries
|
||||
- [ ] Delete `task-images-gallery` element (Line 330)
|
||||
- [ ] Delete `consequence-images-gallery` element (Line 334)
|
||||
- [ ] Update image loading to use `lib-image-gallery`
|
||||
|
||||
#### Step 2.2: Add Category Filtering
|
||||
- [ ] Add task/consequence filtering to `lib-image-gallery`
|
||||
- [ ] Update image loading functions (Lines 4361-4395)
|
||||
|
||||
### **PHASE 3: AUDIO LIBRARY CONSOLIDATION**
|
||||
|
||||
#### Step 3.1: Remove Background/Ambient Galleries
|
||||
- [ ] Delete `background-audio-gallery` element (Line 409)
|
||||
- [ ] Delete `ambient-audio-gallery` element (Line 413)
|
||||
- [ ] Update audio loading to use `lib-audio-gallery`
|
||||
|
||||
#### Step 3.2: Add Category Filtering
|
||||
- [ ] Add background/ambient filtering to `lib-audio-gallery`
|
||||
- [ ] Update audio management functions
|
||||
|
||||
### **PHASE 4: CSS CLEANUP**
|
||||
|
||||
#### Step 4.1: Remove Redundant Styles
|
||||
- [ ] Remove CSS for deleted video gallery systems
|
||||
- [ ] Remove CSS for deleted image gallery systems
|
||||
- [ ] Remove CSS for deleted audio gallery systems
|
||||
- [ ] Keep only `#lib-video-gallery`, `#lib-image-gallery`, `#lib-audio-gallery` styles
|
||||
|
||||
#### Step 4.2: Consolidate Common Styles
|
||||
- [ ] Create unified `.gallery-item` styles
|
||||
- [ ] Create unified `.gallery-container` styles
|
||||
- [ ] Remove duplicate CSS rules
|
||||
|
||||
### **PHASE 5: JAVASCRIPT CLEANUP**
|
||||
|
||||
#### Step 5.1: Function Consolidation
|
||||
- [ ] Merge similar functions from different gallery systems
|
||||
- [ ] Create unified media loading functions
|
||||
- [ ] Standardize event handlers across all galleries
|
||||
|
||||
#### Step 5.2: Remove Dead Code
|
||||
- [ ] Remove unused variables and constants
|
||||
- [ ] Remove commented-out duplicate code
|
||||
- [ ] Clean up console.log debugging statements
|
||||
|
||||
---
|
||||
|
||||
## 📊 ESTIMATED IMPACT
|
||||
|
||||
### **Code Reduction:**
|
||||
- **HTML:** ~200 lines removed (duplicate gallery elements)
|
||||
- **CSS:** ~150 lines removed (redundant styles)
|
||||
- **JavaScript:** ~800 lines removed (duplicate functions)
|
||||
- **Total:** ~1,150 lines of code cleanup
|
||||
|
||||
### **Performance Benefits:**
|
||||
- ✅ Reduced DOM complexity
|
||||
- ✅ Faster page load times
|
||||
- ✅ Simplified debugging
|
||||
- ✅ Consistent user experience
|
||||
- ✅ Easier maintenance
|
||||
|
||||
### **Functionality Preserved:**
|
||||
- ✅ All current video/image/audio management features
|
||||
- ✅ Category filtering (consolidated)
|
||||
- ✅ Selection and deletion capabilities
|
||||
- ✅ Thumbnail display and video info
|
||||
- ✅ Directory linking and management
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ TESTING CHECKLIST
|
||||
|
||||
After each phase, verify:
|
||||
- [ ] Video library loads correctly with thumbnails and info
|
||||
- [ ] Image gallery displays task and consequence images
|
||||
- [ ] Audio gallery shows background and ambient files
|
||||
- [ ] Category filtering works for all media types
|
||||
- [ ] Selection/deletion functions work
|
||||
- [ ] No JavaScript console errors
|
||||
- [ ] CSS styling remains consistent
|
||||
- [ ] Performance is improved
|
||||
|
||||
---
|
||||
|
||||
## 🚀 NEXT STEPS
|
||||
|
||||
1. **Commit Current State** - Save working video library fixes
|
||||
2. **Create Backup Branch** - Before major cleanup
|
||||
3. **Execute Phase 1** - Video library consolidation
|
||||
4. **Test Thoroughly** - Ensure no functionality lost
|
||||
5. **Continue Phases 2-5** - Image, audio, CSS, JS cleanup
|
||||
6. **Final Testing** - Complete system verification
|
||||
|
||||
---
|
||||
|
||||
## 📝 NOTES
|
||||
|
||||
- This cleanup addresses the root cause of our thumbnail spacing issues (multiple conflicting systems)
|
||||
- The `lib-video-gallery` system is confirmed working with proper spacing and video info display
|
||||
- Cleanup will prevent future CSS/JS conflicts between duplicate systems
|
||||
- All functionality will be preserved while significantly reducing code complexity
|
||||
|
||||
---
|
||||
|
||||
*Document created after successful video library thumbnail spacing fix*
|
||||
*Next: Execute cleanup phases to eliminate duplicate media systems*
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
# Gooner Training Academy - Development Roadmap
|
||||
|
||||
## 🎮 Current Status (November 2025)
|
||||
- ✅ **Complete Quick Play System**: Background videos, task management with disable toggles, persistent settings
|
||||
- ✅ **Professional Media Players**: Porn Cinema, Multi-Screen Quad Player, Focus Video Player
|
||||
- ✅ **Task Management**: Disable toggles, consequence integration, ID consistency, debug logging
|
||||
- ✅ **Session Cleanup**: Comprehensive video and popup cleanup when ending sessions
|
||||
- ✅ **Video Integration**: Unified video library with 40+ videos across all game modes
|
||||
- ✅ **XP-based Progression**: Experience system with player statistics and achievements
|
||||
- ✅ **Interactive Task System**: Mirror tasks, focus holds, scenario adventures, photography sessions
|
||||
- ✅ **Desktop Integration**: File management, cross-platform Electron app
|
||||
|
||||
## 🚧 Active Development
|
||||
- Bug fixes and stability enhancements
|
||||
- Performance optimizations
|
||||
- User experience improvements
|
||||
- Code refactoring for maintainability
|
||||
|
||||
## 📋 Feature Backlog
|
||||
|
||||
### 🎯 High Priority (Core Game Polish)
|
||||
- [ ] **Enhanced Task System**
|
||||
- Advanced task editor for custom content creation
|
||||
- Task difficulty rating and progression tracking
|
||||
- Category-based task filtering and organization
|
||||
- Custom task timers and requirements
|
||||
- [ ] **Improved User Interface**
|
||||
- Modern responsive design across all game modes
|
||||
- Better visual feedback and animations
|
||||
- Accessibility features and keyboard navigation
|
||||
- Theme system with multiple color schemes
|
||||
- [ ] **Performance & Stability**
|
||||
- Memory optimization for large media libraries
|
||||
- Better error handling and user feedback
|
||||
- Comprehensive testing and bug fixes
|
||||
- Loading performance improvements
|
||||
|
||||
### 🎨 Medium Priority (User Experience)
|
||||
- [ ] **Advanced Customization**
|
||||
- User settings and preference management
|
||||
- Custom game mode creation tools
|
||||
- Personalized difficulty adjustment
|
||||
- Save/load custom configurations
|
||||
- [ ] **Enhanced Media Management**
|
||||
- Improved image/video organization and tagging
|
||||
- Bulk import/export functionality
|
||||
- Media quality validation and optimization
|
||||
- Content recommendation system
|
||||
- [ ] **Statistics & Analytics**
|
||||
- Detailed progress tracking and achievements
|
||||
- Session history and performance analysis
|
||||
- Goal setting and milestone tracking
|
||||
- Export statistics for external analysis
|
||||
|
||||
### 🎵 Content & Media Enhancements
|
||||
- [ ] **Audio System Improvements**
|
||||
- Custom playlist creation and management
|
||||
- Crossfade and transition effects
|
||||
- Audio visualization and spectrum display
|
||||
- Voice guidance and instruction system
|
||||
- [ ] **Video Feature Expansion**
|
||||
- Enhanced video player controls and effects
|
||||
- Picture-in-picture support for multi-tasking
|
||||
- Video bookmarking and favorites
|
||||
- Synchronized multi-video playback
|
||||
- [ ] **Content Library**
|
||||
- Built-in sample content library
|
||||
- Community content sharing (if appropriate)
|
||||
- Content curation and quality guidelines
|
||||
- Regular content updates and additions
|
||||
|
||||
### 🔧 Technical Improvements
|
||||
- [ ] **Code Quality**
|
||||
- Comprehensive code refactoring and optimization
|
||||
- Automated testing suite implementation
|
||||
- Better documentation and inline comments
|
||||
- TypeScript migration for better type safety
|
||||
- [ ] **Cross-Platform Support**
|
||||
- Mobile-responsive design improvements
|
||||
- Touch interface optimization
|
||||
- PWA (Progressive Web App) capabilities
|
||||
- iOS/Android compatibility testing
|
||||
- [ ] **Security & Privacy**
|
||||
- Enhanced data encryption and protection
|
||||
- Secure backup and recovery features
|
||||
- Privacy-first design principles
|
||||
- Local-only data processing guarantees
|
||||
|
||||
### 🔮 Future Enhancements (Low Priority)
|
||||
- [ ] **Advanced Features**
|
||||
- Machine learning-based personalization
|
||||
- Voice recognition and synthesis integration
|
||||
- Virtual reality (VR) compatibility exploration
|
||||
- Advanced analytics and user behavior insights
|
||||
- [ ] **Community Features**
|
||||
- User-generated content platform
|
||||
- Community challenges and leaderboards
|
||||
- Content sharing and collaboration tools
|
||||
- Modding support and plugin system
|
||||
|
||||
## 🗓️ Version Planning
|
||||
|
||||
### v4.0 - Enhanced User Experience
|
||||
- Advanced task system with custom content creation
|
||||
- Modern UI redesign with accessibility features
|
||||
- Performance optimizations and stability improvements
|
||||
- Comprehensive testing and quality assurance
|
||||
|
||||
### v5.0 - Community & Content
|
||||
- Built-in content library and curation system
|
||||
- Advanced media management and organization
|
||||
- Community features and content sharing
|
||||
- Mobile and cross-platform optimization
|
||||
|
||||
### v6.0+ - Advanced Features
|
||||
- AI-powered personalization and recommendations
|
||||
- Advanced analytics and progress tracking
|
||||
- VR/AR compatibility and immersive features
|
||||
- Professional content creation tools
|
||||
|
||||
---
|
||||
|
||||
*This roadmap is a living document updated as development progresses. Focus remains on core functionality, stability, and user experience.*
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
- volume button doesn't work in quick-play
|
||||
- remove mute/hide buttons from quick play
|
||||
- video still doesn't stop on endGame
|
||||
- Game stats show all zeros on game end
|
||||
- a loading spinner would be nice when starting quick play
|
||||
- Remove the Hentai images from home screen
|
||||
- find clean neon bars/lights for sides of home screen
|
||||
- Rename annoyance to settings
|
||||
- redo settings. (maybe remove)
|
||||
- custom tasks in quick play need to have a duration input
|
||||
- All skip buttons in training scenario should be replaced with a Quit button
|
||||
- There are tasks in the degrading poses that need the pose verification added
|
||||
- add video controls to the background video player in training-academy
|
||||
- add an ability to delete photos from the photo gallery
|
||||
- fix the thumbails in the video library
|
||||
- add a theater mode in porn cinema that hides the playlist side bar and library.
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
# Organized Task List - November 2025
|
||||
|
||||
Based on analysis of the todo list, roadmap, and project structure, here's a systematically organized task list grouped by feature area and priority.
|
||||
|
||||
## 🎮 Quick Play Mode Issues (High Priority)
|
||||
*Core functionality problems affecting the main Quick Play experience*
|
||||
|
||||
### Critical Bugs
|
||||
- [ ] **Fix volume button in quick-play**
|
||||
- *Status: Bug - Core functionality broken*
|
||||
- *Location: Quick Play interface, likely audio controls*
|
||||
- *Impact: User can't control audio volume*
|
||||
|
||||
- [ ] **Video doesn't stop on endGame**
|
||||
- *Status: Bug - Session cleanup incomplete*
|
||||
- *Location: Game state management, video player cleanup*
|
||||
- *Impact: Videos continue playing after game ends*
|
||||
|
||||
- [ ] **Game stats show all zeros on game end**
|
||||
- *Status: Bug - Statistics not being recorded/displayed*
|
||||
- *Location: Player stats system*
|
||||
- *Impact: No progress tracking for users*
|
||||
|
||||
### UI/UX Improvements
|
||||
- [ ] **Remove mute/hide buttons from quick play**
|
||||
- *Status: Enhancement - Streamline interface*
|
||||
- *Location: Quick Play UI controls*
|
||||
- *Priority: Medium*
|
||||
|
||||
- [ ] **Add loading spinner when starting quick play**
|
||||
- *Status: Enhancement - Better user feedback*
|
||||
- *Location: Quick Play initialization*
|
||||
- *Priority: Medium*
|
||||
|
||||
- [ ] **Custom tasks in quick play need duration input**
|
||||
- *Status: Feature - Enhanced customization*
|
||||
- *Location: Task management system*
|
||||
- *Priority: Medium*
|
||||
|
||||
## 🎬 Video & Media Player (Medium Priority)
|
||||
*Professional media system improvements*
|
||||
|
||||
### Video Library & Controls
|
||||
- [ ] **Fix thumbnails in the video library**
|
||||
- *Status: Bug - Visual display issue*
|
||||
- *Location: Video library UI*
|
||||
- *Impact: Poor user experience browsing videos*
|
||||
|
||||
- [ ] **Add video controls to background video player in training-academy**
|
||||
- *Status: Enhancement - User control improvement*
|
||||
- *Location: Training Academy mode*
|
||||
- *Priority: Medium*
|
||||
|
||||
### Porn Cinema Enhancements
|
||||
- [ ] **Add theater mode that hides playlist sidebar and library**
|
||||
- *Status: Feature - Enhanced viewing experience*
|
||||
- *Location: Porn Cinema interface*
|
||||
- *Priority: Low-Medium*
|
||||
|
||||
## 🎯 Training Academy & Tasks (Medium Priority)
|
||||
*Training mode and task system improvements*
|
||||
|
||||
### Task System
|
||||
- [ ] **Add pose verification to degrading poses tasks**
|
||||
- *Status: Feature - Interactive task enhancement*
|
||||
- *Location: Task management system*
|
||||
- *Priority: Medium*
|
||||
|
||||
### Navigation & Controls
|
||||
- [ ] **Replace all skip buttons in training scenario with Quit button**
|
||||
- *Status: Enhancement - Better user control*
|
||||
- *Location: Training Academy interface*
|
||||
- *Priority: Medium*
|
||||
|
||||
## 🖼️ Media Management (Low-Medium Priority)
|
||||
*Image and photo management features*
|
||||
|
||||
### Photo Gallery
|
||||
- [ ] **Add ability to delete photos from photo gallery**
|
||||
- *Status: Feature - Content management*
|
||||
- *Location: Webcam/Photography system*
|
||||
- *Priority: Medium*
|
||||
|
||||
## 🏠 Home Screen & UI (Low Priority)
|
||||
*Main interface and visual improvements*
|
||||
|
||||
### Visual Design
|
||||
- [ ] **Remove Hentai images from home screen**
|
||||
- *Status: Content - Clean up interface*
|
||||
- *Location: Main interface*
|
||||
- *Priority: Low*
|
||||
|
||||
- [ ] **Find clean neon bars/lights for sides of home screen**
|
||||
- *Status: Enhancement - Visual improvement*
|
||||
- *Location: Main interface styling*
|
||||
- *Priority: Low*
|
||||
|
||||
## ⚙️ Settings & Configuration (Low Priority)
|
||||
*Settings system overhaul*
|
||||
|
||||
### Settings System
|
||||
- [ ] **Rename 'annoyance' to 'settings'**
|
||||
- *Status: Enhancement - Better terminology*
|
||||
- *Location: Settings interface*
|
||||
- *Priority: Low*
|
||||
|
||||
- [ ] **Redo settings system (maybe remove)**
|
||||
- *Status: Evaluation - System redesign*
|
||||
- *Location: Settings management*
|
||||
- *Priority: Low*
|
||||
- *Note: Needs evaluation - may conflict with roadmap plans*
|
||||
|
||||
---
|
||||
|
||||
## 📊 Task Priority Matrix
|
||||
|
||||
### 🔴 Critical (Fix Immediately)
|
||||
1. Fix volume button in quick-play
|
||||
2. Video doesn't stop on endGame
|
||||
3. Game stats show all zeros on game end
|
||||
|
||||
### 🟡 High (Next Sprint)
|
||||
4. Add loading spinner for quick play
|
||||
5. Fix thumbnails in video library
|
||||
6. Remove mute/hide buttons from quick play
|
||||
|
||||
### 🟢 Medium (Upcoming)
|
||||
7. Custom task duration input
|
||||
8. Add pose verification to tasks
|
||||
9. Replace skip with quit buttons
|
||||
10. Add video controls to training academy
|
||||
11. Add photo deletion capability
|
||||
|
||||
### 🔵 Low (Future)
|
||||
12. Theater mode for porn cinema
|
||||
13. Remove hentai images from home
|
||||
14. Find clean neon graphics
|
||||
15. Rename annoyance to settings
|
||||
16. Redo settings system
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Recommended Sprint Planning
|
||||
|
||||
### Sprint 1: Critical Bug Fixes (Week 1)
|
||||
Focus on the three critical bugs that are breaking core functionality:
|
||||
- Video control and cleanup issues
|
||||
- Statistics tracking problems
|
||||
- Audio control fixes
|
||||
|
||||
### Sprint 2: User Experience (Week 2)
|
||||
Improve the user experience with:
|
||||
- Loading feedback
|
||||
- UI streamlining
|
||||
- Visual fixes
|
||||
|
||||
### Sprint 3: Feature Enhancements (Week 3-4)
|
||||
Add new capabilities:
|
||||
- Enhanced task system
|
||||
- Better media management
|
||||
- Advanced controls
|
||||
|
||||
### Sprint 4: Polish & Visual (Future)
|
||||
Final polish items:
|
||||
- Visual improvements
|
||||
- Settings overhaul
|
||||
- Interface cleanup
|
||||
|
||||
---
|
||||
|
||||
## 📝 Development Notes
|
||||
|
||||
### Code Locations to Focus On:
|
||||
- **Quick Play**: `src/core/game.js`, Quick Play specific files
|
||||
- **Video System**: `src/features/media/` directory
|
||||
- **Stats**: `src/features/stats/playerStats.js`
|
||||
- **Audio**: `src/features/audio/audioManager.js`
|
||||
- **UI Controls**: Individual HTML files and CSS
|
||||
|
||||
### Testing Strategy:
|
||||
- Test each bug fix in isolation
|
||||
- Verify session cleanup works properly
|
||||
- Check statistics are properly recorded
|
||||
- Test across different game modes
|
||||
|
||||
This organization should help you tackle the most important issues first while building toward the larger roadmap goals.
|
||||
|
|
@ -498,7 +498,7 @@
|
|||
|
||||
<!-- Single unified video gallery (no categories) -->
|
||||
<div class="video-galleries-container">
|
||||
<div id="unified-video-gallery" class="video-gallery active" style="display: grid !important; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)) !important; gap: 12px !important; padding: 16px !important; height: 80vh !important; max-height: 1200px !important; overflow-y: auto !important; visibility: visible !important; opacity: 1 !important; background: rgba(255,0,0,0.1) !important; border: 2px solid lime !important;">
|
||||
<div id="unified-video-gallery" class="video-gallery active">
|
||||
<!-- All videos from all linked directories will be shown here -->
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
634
quick-play.html
634
quick-play.html
|
|
@ -12,15 +12,7 @@
|
|||
<script src="https://unpkg.com/jszip@3.10.1/dist/jszip.min.js" crossorigin="anonymous"></script>
|
||||
</head>
|
||||
<body class="quick-play-mode">
|
||||
<!-- Video Toggle Button -->
|
||||
<button class="video-toggle-btn" id="videoToggleBtn" title="Toggle Video Visibility">
|
||||
👁️
|
||||
</button>
|
||||
|
||||
<!-- Video Mute Button -->
|
||||
<button class="video-mute-btn" id="videoMuteBtn" title="Toggle Video Sound">
|
||||
🔊
|
||||
</button>
|
||||
<!-- Video toggle and mute buttons removed for streamlined interface -->
|
||||
|
||||
<!-- Loading Overlay -->
|
||||
<div id="quick-play-loading" class="loading-overlay">
|
||||
|
|
@ -1032,28 +1024,19 @@
|
|||
|
||||
const video = document.getElementById('background-video');
|
||||
|
||||
// Check if mute button exists and get current mute state
|
||||
const muteBtn = document.getElementById('videoMuteBtn');
|
||||
const shouldBeMuted = muteBtn && muteBtn.textContent === '🔇';
|
||||
// Video starts with settings-based mute state (no external mute button)
|
||||
|
||||
// Set volume and mute state
|
||||
video.volume = videoVolume;
|
||||
if (shouldBeMuted) {
|
||||
video.muted = true;
|
||||
} else if (quickPlaySettings.enableVideoSound) {
|
||||
video.muted = false;
|
||||
// Get volume from slider if it exists, otherwise use default
|
||||
const volumeSlider = document.getElementById('video-volume');
|
||||
let targetVolume = videoVolume;
|
||||
if (volumeSlider) {
|
||||
targetVolume = (volumeSlider.value / 100) * (quickPlaySettings.enableVideoSound ? 1 : 0);
|
||||
console.log(`🔊 Using volume from slider: ${volumeSlider.value}% -> ${targetVolume}`);
|
||||
}
|
||||
|
||||
// Update mute button state for new video
|
||||
if (muteBtn) {
|
||||
if (video.muted) {
|
||||
muteBtn.textContent = '🔇';
|
||||
muteBtn.title = 'Video Sound Off';
|
||||
} else {
|
||||
muteBtn.textContent = '🔊';
|
||||
muteBtn.title = 'Video Sound On';
|
||||
}
|
||||
}
|
||||
// Set volume and mute state based on settings
|
||||
video.volume = targetVolume;
|
||||
video.muted = !quickPlaySettings.enableVideoSound || targetVolume === 0;
|
||||
|
||||
video.onloadstart = () => {
|
||||
console.log('🎬 Video loading started');
|
||||
|
|
@ -1067,6 +1050,19 @@
|
|||
console.log('🎬 Video metadata loaded');
|
||||
updateVideoInfo();
|
||||
updateVideoProgress();
|
||||
|
||||
// Sync volume control with video
|
||||
const volumeSlider = document.getElementById('video-volume');
|
||||
const volumeDisplay = document.getElementById('video-volume-display');
|
||||
if (volumeSlider && volumeDisplay) {
|
||||
const currentVolume = Math.round(video.volume * 100);
|
||||
volumeSlider.value = currentVolume;
|
||||
volumeDisplay.textContent = currentVolume + '%';
|
||||
console.log(`🔊 Synced volume control to ${currentVolume}%`);
|
||||
}
|
||||
|
||||
// Dispatch custom event for volume control initialization
|
||||
document.dispatchEvent(new CustomEvent('videoLoaded'));
|
||||
};
|
||||
|
||||
video.ontimeupdate = () => {
|
||||
|
|
@ -1094,15 +1090,104 @@
|
|||
}
|
||||
|
||||
function setupBackgroundVideoListeners() {
|
||||
// Simple setup for direct video element
|
||||
// Set up a function to initialize controls when video container is ready
|
||||
const initControlsWhenReady = () => {
|
||||
// Random video button
|
||||
const randomBtn = document.querySelector('.random-video-btn');
|
||||
if (randomBtn) {
|
||||
randomBtn.addEventListener('click', () => {
|
||||
loadRandomBackgroundVideo();
|
||||
});
|
||||
}
|
||||
|
||||
// Background video controls volume slider
|
||||
const backgroundVolumeSlider = document.querySelector('.background-video-controls .volume-slider');
|
||||
if (backgroundVolumeSlider) {
|
||||
backgroundVolumeSlider.addEventListener('input', (e) => {
|
||||
const currentVideo = document.getElementById('background-video');
|
||||
if (currentVideo) {
|
||||
const volume = e.target.value / 100;
|
||||
currentVideo.volume = volume;
|
||||
console.log(`🔊 Background controls volume set to ${e.target.value}% (${volume})`);
|
||||
|
||||
// Sync with main volume control
|
||||
const mainVolumeSlider = document.getElementById('video-volume');
|
||||
const mainVolumeDisplay = document.getElementById('video-volume-display');
|
||||
if (mainVolumeSlider && mainVolumeDisplay) {
|
||||
mainVolumeSlider.value = e.target.value;
|
||||
mainVolumeDisplay.textContent = e.target.value + '%';
|
||||
}
|
||||
|
||||
// Update mute states
|
||||
const backgroundMuteBtn = document.querySelector('.background-video-controls .mute-btn');
|
||||
if (volume === 0) {
|
||||
currentVideo.muted = true;
|
||||
if (backgroundMuteBtn) {
|
||||
backgroundMuteBtn.textContent = '🔇';
|
||||
}
|
||||
} else if (currentVideo.muted) {
|
||||
currentVideo.muted = false;
|
||||
if (backgroundMuteBtn) {
|
||||
backgroundMuteBtn.textContent = '🔊';
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
console.log('✅ Background video volume slider listener set up');
|
||||
}
|
||||
|
||||
// Background video mute button
|
||||
const backgroundMuteBtn = document.querySelector('.background-video-controls .mute-btn');
|
||||
if (backgroundMuteBtn) {
|
||||
backgroundMuteBtn.addEventListener('click', () => {
|
||||
const currentVideo = document.getElementById('background-video');
|
||||
if (currentVideo) {
|
||||
if (currentVideo.muted) {
|
||||
currentVideo.muted = false;
|
||||
backgroundMuteBtn.textContent = '🔊';
|
||||
console.log('🔊 Background video unmuted');
|
||||
} else {
|
||||
currentVideo.muted = true;
|
||||
backgroundMuteBtn.textContent = '🔇';
|
||||
console.log('🔇 Background video muted');
|
||||
}
|
||||
|
||||
// Main mute button removed for streamlined interface
|
||||
}
|
||||
});
|
||||
console.log('✅ Background video mute button listener set up');
|
||||
}
|
||||
|
||||
// Background video play/pause button
|
||||
const backgroundPlayPauseBtn = document.querySelector('.background-video-controls .play-pause-btn');
|
||||
if (backgroundPlayPauseBtn) {
|
||||
backgroundPlayPauseBtn.addEventListener('click', async () => {
|
||||
const currentVideo = document.getElementById('background-video');
|
||||
if (currentVideo) {
|
||||
try {
|
||||
if (currentVideo.paused) {
|
||||
await currentVideo.play();
|
||||
backgroundPlayPauseBtn.textContent = '⏸️';
|
||||
console.log('▶️ Background video resumed');
|
||||
} else {
|
||||
currentVideo.pause();
|
||||
backgroundPlayPauseBtn.textContent = '▶️';
|
||||
console.log('⏸️ Background video paused');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Background video playback error:', error);
|
||||
}
|
||||
}
|
||||
});
|
||||
console.log('✅ Background video play/pause button listener set up');
|
||||
}
|
||||
};
|
||||
|
||||
// Random video button
|
||||
const randomBtn = document.querySelector('.random-video-btn');
|
||||
if (randomBtn) {
|
||||
randomBtn.addEventListener('click', () => {
|
||||
loadRandomBackgroundVideo();
|
||||
});
|
||||
}
|
||||
// Try to initialize now and also set up for later
|
||||
initControlsWhenReady();
|
||||
|
||||
// Also set up when video loads
|
||||
document.addEventListener('videoLoaded', initControlsWhenReady);
|
||||
}
|
||||
|
||||
function updateVideoOptionsVisibility() {
|
||||
|
|
@ -1293,53 +1378,9 @@
|
|||
}
|
||||
|
||||
function initializeVideoToggle() {
|
||||
const toggleBtn = document.getElementById('videoToggleBtn');
|
||||
if (toggleBtn) {
|
||||
toggleBtn.addEventListener('click', () => {
|
||||
const video = document.querySelector('.background-video');
|
||||
if (!video) return;
|
||||
|
||||
if (currentVideoOpacity === 'hidden') {
|
||||
// Show video at normal opacity
|
||||
currentVideoOpacity = 'normal';
|
||||
video.classList.remove('video-hidden');
|
||||
video.classList.add('video-normal');
|
||||
toggleBtn.textContent = '👁️';
|
||||
toggleBtn.title = 'Video Visible';
|
||||
} else {
|
||||
// Hide video
|
||||
currentVideoOpacity = 'hidden';
|
||||
video.classList.remove('video-dim', 'video-normal', 'video-bright');
|
||||
video.classList.add('video-hidden');
|
||||
toggleBtn.textContent = '🙈';
|
||||
toggleBtn.title = 'Video Hidden';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize mute button
|
||||
const muteBtn = document.getElementById('videoMuteBtn');
|
||||
if (muteBtn) {
|
||||
muteBtn.addEventListener('click', () => {
|
||||
const video = document.getElementById('backgroundVideo');
|
||||
if (!video) return;
|
||||
|
||||
if (video.muted) {
|
||||
// Unmute video
|
||||
video.muted = false;
|
||||
video.volume = quickPlaySettings.enableVideoSound ? 0.3 : 0.1;
|
||||
muteBtn.textContent = '🔊';
|
||||
muteBtn.title = 'Video Sound On';
|
||||
console.log('🔊 Video unmuted');
|
||||
} else {
|
||||
// Mute video
|
||||
video.muted = true;
|
||||
muteBtn.textContent = '🔇';
|
||||
muteBtn.title = 'Video Sound Off';
|
||||
console.log('🔇 Video muted');
|
||||
}
|
||||
});
|
||||
}
|
||||
// Video toggle and mute buttons removed for streamlined interface
|
||||
// Video controls are now available through the main video control panel
|
||||
console.log('<27> Video control buttons removed for streamlined interface');
|
||||
}
|
||||
|
||||
function showVideoSetupNotification() {
|
||||
|
|
@ -1619,17 +1660,74 @@
|
|||
function startQuickPlaySession() {
|
||||
console.log('⚡ Starting Quick Play session with settings:', quickPlaySettings);
|
||||
|
||||
// Show loading overlay during session startup
|
||||
const loadingOverlay = document.getElementById('quick-play-loading');
|
||||
const loadingStatus = document.getElementById('loading-status');
|
||||
const loadingProgress = document.getElementById('loading-progress-fill');
|
||||
const loadingPercentage = document.getElementById('loading-percentage');
|
||||
|
||||
if (loadingOverlay) {
|
||||
loadingOverlay.style.display = 'flex';
|
||||
if (loadingStatus) loadingStatus.textContent = 'Initializing your session...';
|
||||
if (loadingProgress) loadingProgress.style.width = '0%';
|
||||
if (loadingPercentage) loadingPercentage.textContent = '0%';
|
||||
}
|
||||
|
||||
// Reset session stats for new session
|
||||
sessionStats = {
|
||||
started: Date.now(),
|
||||
completed: 0,
|
||||
skipped: 0,
|
||||
xp: 0
|
||||
};
|
||||
console.log('📊 Session stats reset:', sessionStats);
|
||||
|
||||
// Save current settings
|
||||
localStorage.setItem('quickPlaySettings', JSON.stringify(quickPlaySettings));
|
||||
|
||||
// Hide setup screen, show game screen
|
||||
document.getElementById('quick-play-setup').style.display = 'none';
|
||||
document.getElementById('quick-play-game').style.display = 'block';
|
||||
document.getElementById('game-status-bar').style.display = 'flex';
|
||||
document.getElementById('pause-game-btn').style.display = 'inline-block';
|
||||
|
||||
// Initialize and start the game
|
||||
initializeGameInstance();
|
||||
// Simulate loading progress
|
||||
let progress = 0;
|
||||
const progressInterval = setInterval(() => {
|
||||
progress += Math.random() * 20 + 10; // Random progress between 10-30% per step
|
||||
if (progress >= 100) {
|
||||
progress = 100;
|
||||
clearInterval(progressInterval);
|
||||
}
|
||||
|
||||
if (loadingProgress) loadingProgress.style.width = progress + '%';
|
||||
if (loadingPercentage) loadingPercentage.textContent = Math.round(progress) + '%';
|
||||
|
||||
// Update status messages
|
||||
if (loadingStatus) {
|
||||
if (progress < 30) {
|
||||
loadingStatus.textContent = 'Loading game configuration...';
|
||||
} else if (progress < 60) {
|
||||
loadingStatus.textContent = 'Preparing video systems...';
|
||||
} else if (progress < 90) {
|
||||
loadingStatus.textContent = 'Setting up task system...';
|
||||
} else {
|
||||
loadingStatus.textContent = 'Almost ready...';
|
||||
}
|
||||
}
|
||||
|
||||
if (progress >= 100) {
|
||||
setTimeout(() => {
|
||||
// Hide setup screen, show game screen
|
||||
document.getElementById('quick-play-setup').style.display = 'none';
|
||||
document.getElementById('quick-play-game').style.display = 'block';
|
||||
document.getElementById('game-status-bar').style.display = 'flex';
|
||||
document.getElementById('pause-game-btn').style.display = 'inline-block';
|
||||
|
||||
// Hide loading overlay
|
||||
if (loadingOverlay) {
|
||||
loadingOverlay.style.display = 'none';
|
||||
}
|
||||
|
||||
// Initialize and start the game
|
||||
initializeGameInstance();
|
||||
}, 500);
|
||||
}
|
||||
}, 200); // Update every 200ms
|
||||
}
|
||||
|
||||
function initializeGameInstance() {
|
||||
|
|
@ -1914,14 +2012,39 @@
|
|||
const volumeDisplay = document.getElementById('video-volume-display');
|
||||
if (volumeSlider && volumeDisplay) {
|
||||
volumeSlider.addEventListener('input', (e) => {
|
||||
const volume = e.target.value / 100;
|
||||
video.volume = volume;
|
||||
volumeDisplay.textContent = e.target.value + '%';
|
||||
const currentVideo = document.getElementById('background-video');
|
||||
if (currentVideo) {
|
||||
const volume = e.target.value / 100;
|
||||
currentVideo.volume = volume;
|
||||
volumeDisplay.textContent = e.target.value + '%';
|
||||
console.log(`🔊 Volume set to ${e.target.value}% (${volume})`);
|
||||
|
||||
// Auto-mute when volume is 0
|
||||
if (volume === 0) {
|
||||
currentVideo.muted = true;
|
||||
} else if (currentVideo.muted && volume > 0) {
|
||||
currentVideo.muted = false;
|
||||
}
|
||||
} else {
|
||||
console.warn('⚠️ No background video element found for volume control');
|
||||
}
|
||||
});
|
||||
|
||||
// Set initial volume
|
||||
video.volume = volumeSlider.value / 100;
|
||||
volumeDisplay.textContent = volumeSlider.value + '%';
|
||||
// Set initial volume when video loads
|
||||
const initializeVolume = () => {
|
||||
const currentVideo = document.getElementById('background-video');
|
||||
if (currentVideo) {
|
||||
currentVideo.volume = volumeSlider.value / 100;
|
||||
volumeDisplay.textContent = volumeSlider.value + '%';
|
||||
console.log(`🔊 Initial volume set to ${volumeSlider.value}%`);
|
||||
}
|
||||
};
|
||||
|
||||
// Try to set initial volume now and also when video loads
|
||||
initializeVolume();
|
||||
|
||||
// Also set up a listener for when new videos are loaded
|
||||
document.addEventListener('videoLoaded', initializeVolume);
|
||||
}
|
||||
|
||||
// Playlist selection
|
||||
|
|
@ -2247,21 +2370,13 @@
|
|||
try {
|
||||
let tasks = null;
|
||||
|
||||
// Get tasks using consistent source
|
||||
tasks = getTasksFromSource('main');
|
||||
// Get all tasks including custom ones
|
||||
tasks = getAllTasksForGame('main');
|
||||
|
||||
if (tasks && tasks.length > 0) {
|
||||
// Apply tag filtering
|
||||
// Apply tag filtering (disabled tasks already filtered out)
|
||||
let filteredTasks = [...tasks];
|
||||
|
||||
// Step 0: Remove disabled tasks
|
||||
filteredTasks = filteredTasks.filter((task) => {
|
||||
const isDisabled = quickPlaySettings.disabledTasks &&
|
||||
quickPlaySettings.disabledTasks.main &&
|
||||
quickPlaySettings.disabledTasks.main.includes(task.id);
|
||||
return !isDisabled;
|
||||
});
|
||||
|
||||
// Step 1: If include tags are selected, filter to only those tasks
|
||||
if (quickPlaySettings.includeTags && quickPlaySettings.includeTags.length > 0) {
|
||||
filteredTasks = filteredTasks.filter(task => {
|
||||
|
|
@ -2471,18 +2586,9 @@
|
|||
function loadConsequenceTask() {
|
||||
console.log('🚨 Loading consequence task for skipping');
|
||||
|
||||
// Get consequence tasks using consistent source
|
||||
let consequenceTasks = getTasksFromSource('consequence');
|
||||
|
||||
// Filter out disabled consequence tasks
|
||||
if (quickPlaySettings.disabledTasks && quickPlaySettings.disabledTasks.consequence) {
|
||||
consequenceTasks = consequenceTasks.filter(task =>
|
||||
!quickPlaySettings.disabledTasks.consequence.includes(task.id)
|
||||
);
|
||||
console.log('📋 Filtered consequence tasks - enabled count:', consequenceTasks.length);
|
||||
} else {
|
||||
console.log('📋 Got consequence tasks from source:', consequenceTasks.length);
|
||||
}
|
||||
// Get all consequence tasks including custom ones (disabled tasks already filtered)
|
||||
let consequenceTasks = getAllTasksForGame('consequence');
|
||||
console.log('📋 Got consequence tasks (including custom) - enabled count:', consequenceTasks.length);
|
||||
|
||||
if (consequenceTasks.length === 0) {
|
||||
console.warn('⚠️ No consequence tasks available, using fallback');
|
||||
|
|
@ -2877,6 +2983,14 @@
|
|||
console.log('endGame called - starting game termination');
|
||||
|
||||
try {
|
||||
// Immediately mute all videos to stop audio instantly
|
||||
const allVideos = document.querySelectorAll('video');
|
||||
allVideos.forEach(video => {
|
||||
video.muted = true;
|
||||
video.pause();
|
||||
});
|
||||
console.log(`Immediately muted and paused ${allVideos.length} video elements`);
|
||||
|
||||
// Immediately set flag to prevent multiple calls
|
||||
if (window.isEndingGame) {
|
||||
console.log('Game already ending, ignoring duplicate call');
|
||||
|
|
@ -2910,7 +3024,7 @@
|
|||
|
||||
// Clean up background video (check if it exists first)
|
||||
if (typeof backgroundVideoPlayer !== 'undefined' && backgroundVideoPlayer) {
|
||||
console.log('Stopping background video');
|
||||
console.log('Stopping background video via backgroundVideoPlayer');
|
||||
try {
|
||||
backgroundVideoPlayer.pause();
|
||||
if (backgroundVideoPlayer.destroy) {
|
||||
|
|
@ -2918,10 +3032,42 @@
|
|||
}
|
||||
backgroundVideoPlayer = null;
|
||||
} catch (videoError) {
|
||||
console.warn('Error stopping background video:', videoError);
|
||||
console.warn('Error stopping background video via backgroundVideoPlayer:', videoError);
|
||||
}
|
||||
} else {
|
||||
console.log('No backgroundVideoPlayer found - skipping background video cleanup');
|
||||
console.log('No backgroundVideoPlayer found - checking for background video element directly');
|
||||
}
|
||||
|
||||
// Quick Play specific background video cleanup
|
||||
const backgroundVideo = document.getElementById('background-video');
|
||||
if (backgroundVideo) {
|
||||
console.log('Found Quick Play background video - stopping it');
|
||||
try {
|
||||
backgroundVideo.pause();
|
||||
backgroundVideo.currentTime = 0;
|
||||
backgroundVideo.src = '';
|
||||
const source = backgroundVideo.querySelector('source');
|
||||
if (source) {
|
||||
source.src = '';
|
||||
}
|
||||
console.log('✅ Quick Play background video stopped and cleared');
|
||||
} catch (bgVideoError) {
|
||||
console.warn('Error stopping Quick Play background video:', bgVideoError);
|
||||
}
|
||||
} else {
|
||||
console.log('No Quick Play background video element found');
|
||||
}
|
||||
|
||||
// Also clean up the background video container
|
||||
const backgroundVideoContainer = document.getElementById('background-video-container');
|
||||
if (backgroundVideoContainer) {
|
||||
console.log('Clearing background video container');
|
||||
try {
|
||||
backgroundVideoContainer.innerHTML = '<div style="opacity: 0;">Video stopped</div>';
|
||||
console.log('✅ Background video container cleared');
|
||||
} catch (containerError) {
|
||||
console.warn('Error clearing background video container:', containerError);
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up video player manager - THIS IS THE IMPORTANT ONE
|
||||
|
|
@ -2935,19 +3081,35 @@
|
|||
}
|
||||
} else {
|
||||
console.log('VideoPlayerManager not found - trying manual video cleanup');
|
||||
// Manual fallback - find and stop all video elements
|
||||
try {
|
||||
const allVideos = document.querySelectorAll('video');
|
||||
allVideos.forEach((video, index) => {
|
||||
console.log(`Stopping video element ${index}:`, video.id || video.src);
|
||||
}
|
||||
|
||||
// Manual fallback - find and stop all video elements (do this regardless)
|
||||
try {
|
||||
const allVideos = document.querySelectorAll('video');
|
||||
console.log(`Found ${allVideos.length} video elements to stop`);
|
||||
allVideos.forEach((video, index) => {
|
||||
const videoId = video.id || 'unknown';
|
||||
const videoSrc = video.src || video.currentSrc || 'no source';
|
||||
console.log(`Stopping video element ${index}: ID="${videoId}", src="${videoSrc}"`);
|
||||
|
||||
try {
|
||||
video.pause();
|
||||
video.currentTime = 0;
|
||||
video.muted = true; // Mute first to stop audio immediately
|
||||
video.src = '';
|
||||
});
|
||||
console.log(`Stopped ${allVideos.length} video elements manually`);
|
||||
} catch (manualError) {
|
||||
console.warn('Error in manual video cleanup:', manualError);
|
||||
}
|
||||
// Remove source elements too
|
||||
const sources = video.querySelectorAll('source');
|
||||
sources.forEach(source => source.src = '');
|
||||
video.load(); // Force reload to clear the video
|
||||
|
||||
console.log(`✅ Stopped video ${index} (${videoId})`);
|
||||
} catch (singleVideoError) {
|
||||
console.warn(`❌ Error stopping video ${index} (${videoId}):`, singleVideoError);
|
||||
}
|
||||
});
|
||||
console.log(`✅ Manual video cleanup complete - processed ${allVideos.length} videos`);
|
||||
} catch (manualError) {
|
||||
console.warn('❌ Error in manual video cleanup:', manualError);
|
||||
}
|
||||
|
||||
// Clean up periodic popups and any active popups
|
||||
|
|
@ -2970,14 +3132,25 @@
|
|||
const tasksElement = document.getElementById('tasks-completed');
|
||||
const xpElement = document.getElementById('current-xp');
|
||||
|
||||
// Calculate actual session time
|
||||
const sessionDurationMs = Date.now() - sessionStats.started;
|
||||
const sessionMinutes = Math.floor(sessionDurationMs / 60000);
|
||||
const sessionSeconds = Math.floor((sessionDurationMs % 60000) / 1000);
|
||||
const formattedSessionTime = `${sessionMinutes.toString().padStart(2, '0')}:${sessionSeconds.toString().padStart(2, '0')}`;
|
||||
|
||||
const results = {
|
||||
sessionTime: timer ? timer.textContent : '00:00',
|
||||
tasksCompleted: gameInstance?.gameState?.tasksCompleted || (tasksElement ? parseInt(tasksElement.textContent) || 0 : 0),
|
||||
xpEarned: gameInstance?.gameState?.xp || (xpElement ? parseInt(xpElement.textContent) || 0 : 0),
|
||||
sessionTime: formattedSessionTime,
|
||||
tasksCompleted: sessionStats.completed || 0,
|
||||
xpEarned: (sessionStats.xp || 0) + (sessionTimeXP || 0),
|
||||
performance: 'Session Complete!',
|
||||
sessionTimeXP: sessionTimeXP
|
||||
sessionTimeXP: sessionTimeXP,
|
||||
skipped: sessionStats.skipped || 0
|
||||
};
|
||||
|
||||
console.log('📊 Final session stats used for results:', sessionStats);
|
||||
console.log('📊 Session time calculation: duration=', sessionDurationMs, 'ms, formatted=', formattedSessionTime);
|
||||
console.log('📊 Calculated results object:', results);
|
||||
|
||||
console.log('Game ended with results:', results);
|
||||
|
||||
// Track session completion in player stats
|
||||
|
|
@ -2990,6 +3163,25 @@
|
|||
});
|
||||
}
|
||||
|
||||
// Final safety cleanup - double-check all videos are stopped
|
||||
setTimeout(() => {
|
||||
console.log('🔍 Final video cleanup check...');
|
||||
const remainingVideos = document.querySelectorAll('video');
|
||||
if (remainingVideos.length > 0) {
|
||||
console.log(`⚠️ Found ${remainingVideos.length} remaining videos - forcing stop`);
|
||||
remainingVideos.forEach((video, index) => {
|
||||
if (!video.paused) {
|
||||
console.log(`🛑 Force stopping video ${index}: ${video.id || 'unnamed'}`);
|
||||
video.pause();
|
||||
video.src = '';
|
||||
video.load();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log('✅ No remaining videos found - cleanup successful');
|
||||
}
|
||||
}, 100);
|
||||
|
||||
// Reset the ending flag after a delay
|
||||
setTimeout(() => {
|
||||
window.isEndingGame = false;
|
||||
|
|
@ -3212,24 +3404,34 @@
|
|||
function showResultsScreen(results) {
|
||||
isGameRunning = false;
|
||||
|
||||
console.log('📊 Showing results screen with data:', results);
|
||||
|
||||
// Hide game screen, show results
|
||||
document.getElementById('quick-play-game').style.display = 'none';
|
||||
document.getElementById('quick-play-results').style.display = 'block';
|
||||
document.getElementById('game-status-bar').style.display = 'none';
|
||||
document.getElementById('pause-game-btn').style.display = 'none';
|
||||
|
||||
// Populate results
|
||||
// Populate results with detailed logging
|
||||
if (results.sessionTime) {
|
||||
document.getElementById('final-session-time').textContent = results.sessionTime;
|
||||
console.log('✅ Set session time:', results.sessionTime);
|
||||
}
|
||||
if (results.tasksCompleted !== undefined) {
|
||||
document.getElementById('final-tasks-count').textContent = results.tasksCompleted;
|
||||
console.log('✅ Set tasks completed:', results.tasksCompleted);
|
||||
}
|
||||
if (results.xpEarned !== undefined) {
|
||||
document.getElementById('final-xp-earned').textContent = results.xpEarned;
|
||||
console.log('✅ Set XP earned:', results.xpEarned);
|
||||
}
|
||||
if (results.performance) {
|
||||
document.getElementById('final-performance').textContent = results.performance;
|
||||
let performanceText = results.performance;
|
||||
if (results.skipped && results.skipped > 0) {
|
||||
performanceText += ` (${results.skipped} skipped)`;
|
||||
}
|
||||
document.getElementById('final-performance').textContent = performanceText;
|
||||
console.log('✅ Set performance:', performanceText);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3829,6 +4031,46 @@
|
|||
return tasks;
|
||||
}
|
||||
|
||||
function getAllTasksForGame(type = 'main') {
|
||||
let tasks = [];
|
||||
|
||||
// Get preset tasks
|
||||
const presetTasks = getTasksFromSource(type);
|
||||
tasks = presetTasks.map(task => {
|
||||
const isDisabled = quickPlaySettings.disabledTasks &&
|
||||
quickPlaySettings.disabledTasks[type] &&
|
||||
quickPlaySettings.disabledTasks[type].includes(task.id);
|
||||
|
||||
return {
|
||||
...task,
|
||||
isCustom: false,
|
||||
type: type,
|
||||
enabled: !isDisabled
|
||||
};
|
||||
});
|
||||
|
||||
// Add custom tasks
|
||||
if (quickPlaySettings.customTasks && quickPlaySettings.customTasks[type]) {
|
||||
const customTasks = quickPlaySettings.customTasks[type].map(task => {
|
||||
const isDisabled = quickPlaySettings.disabledTasks &&
|
||||
quickPlaySettings.disabledTasks[type] &&
|
||||
quickPlaySettings.disabledTasks[type].includes(task.id);
|
||||
|
||||
return {
|
||||
...task,
|
||||
enabled: !isDisabled
|
||||
};
|
||||
});
|
||||
|
||||
tasks = tasks.concat(customTasks);
|
||||
}
|
||||
|
||||
// Filter out disabled tasks
|
||||
tasks = tasks.filter(task => task.enabled !== false);
|
||||
|
||||
return tasks;
|
||||
}
|
||||
|
||||
function showTaskManagement() {
|
||||
console.log('📝 Opening task management');
|
||||
document.getElementById('quick-play-setup').style.display = 'none';
|
||||
|
|
@ -3901,6 +4143,11 @@
|
|||
|
||||
// Get selected tags from dropdown
|
||||
const selectedTags = Array.from(tagsDropdown.selectedOptions).map(option => option.value);
|
||||
|
||||
// Assign random duration based on task type
|
||||
const minDuration = type === 'main' ? 180 : 120; // 3-8 minutes for main tasks, 2-5 minutes for consequence tasks
|
||||
const maxDuration = type === 'main' ? 480 : 300;
|
||||
const duration = Math.floor(Math.random() * (maxDuration - minDuration + 1)) + minDuration;
|
||||
|
||||
if (!taskText) {
|
||||
alert('Please enter a task description');
|
||||
|
|
@ -3913,6 +4160,7 @@
|
|||
text: taskText,
|
||||
description: taskText,
|
||||
tags: selectedTags,
|
||||
duration: duration, // Duration in seconds - randomly assigned
|
||||
isCustom: true,
|
||||
type: type
|
||||
};
|
||||
|
|
@ -3993,9 +4241,11 @@
|
|||
<div class="task-item ${task.enabled === false ? 'disabled' : ''}" data-task-id="${task.id}">
|
||||
<div class="task-content">
|
||||
<div class="task-text">${task.text || task.description || 'Unnamed Task'}</div>
|
||||
<div class="task-tags">
|
||||
${(task.tags || []).map(tag => `<span class="task-tag">${tag}</span>`).join('')}
|
||||
${!task.isCustom ? '<span class="task-tag preset-tag">preset</span>' : '<span class="task-tag custom-tag">custom</span>'}
|
||||
<div class="task-meta">
|
||||
<div class="task-tags">
|
||||
${(task.tags || []).map(tag => `<span class="task-tag">${tag}</span>`).join('')}
|
||||
${!task.isCustom ? '<span class="task-tag preset-tag">preset</span>' : '<span class="task-tag custom-tag">custom</span>'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="task-actions">
|
||||
|
|
@ -4988,6 +5238,7 @@
|
|||
border-radius: 2px;
|
||||
outline: none;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
.volume-slider::-webkit-slider-thumb {
|
||||
|
|
@ -5399,53 +5650,7 @@
|
|||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Quick Video Toggle Button */
|
||||
.video-toggle-btn {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
color: #333;
|
||||
border: 2px solid rgba(0, 0, 0, 0.3);
|
||||
border-radius: 50%;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
z-index: 10000;
|
||||
transition: all 0.3s ease;
|
||||
backdrop-filter: blur(5px);
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.video-toggle-btn:hover {
|
||||
background: rgba(255, 255, 255, 1);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
/* Video Mute Button */
|
||||
.video-mute-btn {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 80px;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
color: #333;
|
||||
border: 2px solid rgba(0, 0, 0, 0.3);
|
||||
border-radius: 50%;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
z-index: 10000;
|
||||
transition: all 0.3s ease;
|
||||
backdrop-filter: blur(5px);
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.video-mute-btn:hover {
|
||||
background: rgba(255, 255, 255, 1);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
/* Video toggle and mute buttons removed for streamlined interface */
|
||||
|
||||
/* Video visibility states */
|
||||
.background-video.video-hidden {
|
||||
|
|
@ -6160,6 +6365,57 @@
|
|||
border-color: rgba(255, 50, 50, 1);
|
||||
box-shadow: 0 0 15px rgba(255, 50, 50, 0.5);
|
||||
}
|
||||
|
||||
/* Duration Input Styling */
|
||||
.duration-input-container {
|
||||
margin-top: 10px;
|
||||
padding: 10px;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border-radius: 5px;
|
||||
border: 1px solid rgba(255, 107, 53, 0.3);
|
||||
}
|
||||
|
||||
.duration-input-container label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
color: #ff6b35;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.duration-input-container input {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
border: 1px solid rgba(255, 107, 53, 0.5);
|
||||
border-radius: 3px;
|
||||
color: #ffffff;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.duration-input-container input:focus {
|
||||
outline: none;
|
||||
border-color: #ff6b35;
|
||||
box-shadow: 0 0 5px rgba(255, 107, 53, 0.3);
|
||||
}
|
||||
|
||||
.task-meta {
|
||||
margin-top: 8px;
|
||||
padding: 5px 0;
|
||||
font-size: 12px;
|
||||
color: #cccccc;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.task-duration {
|
||||
display: inline-block;
|
||||
background: rgba(255, 107, 53, 0.2);
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
margin-right: 8px;
|
||||
color: #ff6b35;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -788,11 +788,12 @@ class TaskChallengeGame {
|
|||
]
|
||||
},
|
||||
defiant_path: {
|
||||
type: 'action',
|
||||
type: 'verification-required',
|
||||
mood: 'dominant',
|
||||
story: "Your defiance is noted with displeasure. 'So be it. We'll break that attitude. Strip completely and assume a humiliating position on all fours. Hold it for 2 full minutes while contemplating your disobedience.'",
|
||||
actionText: "Humiliating position - naked on all fours",
|
||||
duration: 120,
|
||||
verificationInstructions: "Strip completely and get on all fours in a humiliating position",
|
||||
verificationDuration: 90,
|
||||
verificationText: "Hold this humiliating all-fours position while contemplating your disobedience",
|
||||
nextStep: "broken_defiance"
|
||||
},
|
||||
nipple_punishment: {
|
||||
|
|
@ -804,11 +805,12 @@ class TaskChallengeGame {
|
|||
nextStep: "punishment_continues"
|
||||
},
|
||||
position_punishment: {
|
||||
type: 'action',
|
||||
type: 'verification-required',
|
||||
mood: 'degrading',
|
||||
story: "Get on your knees, spread them wide, and put your hands behind your head. Arch your back and present yourself shamefully. Hold this degrading position and think about what a pathetic display you're making.",
|
||||
actionText: "Humiliating presentation position",
|
||||
duration: 45,
|
||||
verificationInstructions: "Kneel with knees spread wide, hands behind head, arch your back shamefully",
|
||||
verificationDuration: 40,
|
||||
verificationText: "Maintain this humiliating presentation position while thinking about what a pathetic display you're making",
|
||||
nextStep: "punishment_continues"
|
||||
},
|
||||
denial_punishment: {
|
||||
|
|
@ -1010,11 +1012,12 @@ class TaskChallengeGame {
|
|||
]
|
||||
},
|
||||
crawling_task: {
|
||||
type: 'action',
|
||||
type: 'verification-required',
|
||||
mood: 'degrading',
|
||||
story: "Get down on your hands and knees. Crawl in a circle around the room like the animal you are. Feel how degrading this position is. You're not a person right now - you're just a pathetic creature on display.",
|
||||
actionText: "Crawling like an animal",
|
||||
duration: 60,
|
||||
verificationInstructions: "Get on hands and knees in a crawling position like an animal",
|
||||
verificationDuration: 50,
|
||||
verificationText: "Maintain this animal-like crawling position while feeling how degrading it is",
|
||||
nextStep: "humiliation_escalates"
|
||||
},
|
||||
verbal_humiliation: {
|
||||
|
|
@ -1029,11 +1032,12 @@ class TaskChallengeGame {
|
|||
mirrorTaskText: "Repeat each phrase 5 times while maintaining eye contact with yourself: 'I am a pathetic toy', 'I exist for others' pleasure', 'I have no dignity'"
|
||||
},
|
||||
position_humiliation: {
|
||||
type: 'action',
|
||||
type: 'verification-required',
|
||||
mood: 'shameful',
|
||||
story: "Assume the most shameful position you can think of. Spread yourself wide, arch your back, present yourself like you're begging for attention. Hold this degrading pose and feel the shame wash over you.",
|
||||
actionText: "Shameful presentation position",
|
||||
duration: 50,
|
||||
verificationInstructions: "Get in a shameful presentation position - spread wide, arch your back, present yourself",
|
||||
verificationDuration: 45,
|
||||
verificationText: "Maintain this degrading position while feeling the shame wash over you",
|
||||
nextStep: "humiliation_escalates"
|
||||
},
|
||||
building_shame: {
|
||||
|
|
@ -1391,19 +1395,21 @@ class TaskChallengeGame {
|
|||
]
|
||||
},
|
||||
resistance_noted: {
|
||||
type: 'action',
|
||||
type: 'verification-required',
|
||||
mood: 'stern',
|
||||
story: "Your resistance is noted and will be addressed. First, you'll learn what happens to those who resist. Assume a stress position - hands behind head, standing on toes for 60 seconds.",
|
||||
actionText: "Stress position for resistance",
|
||||
duration: 60,
|
||||
verificationInstructions: "Stand on your toes with hands behind head in a stress position",
|
||||
verificationDuration: 55,
|
||||
verificationText: "Maintain this challenging stress position to learn what happens to those who resist",
|
||||
nextStep: "resistance_broken"
|
||||
},
|
||||
position_training: {
|
||||
type: 'action',
|
||||
type: 'verification-required',
|
||||
mood: 'instructional',
|
||||
story: "When I say 'present', you will immediately assume a kneeling position with hands on thighs. When I say 'attention', stand straight with hands at sides. Practice these transitions for 45 seconds.",
|
||||
actionText: "Position command training",
|
||||
duration: 45,
|
||||
verificationInstructions: "Start in kneeling position with hands on thighs - demonstrate proper 'present' position",
|
||||
verificationDuration: 40,
|
||||
verificationText: "Hold the proper 'present' position to show you understand position command training",
|
||||
nextStep: "training_progresses"
|
||||
},
|
||||
response_training: {
|
||||
|
|
|
|||
|
|
@ -399,6 +399,7 @@ class VideoLibrary {
|
|||
}
|
||||
|
||||
createVideoElement(video) {
|
||||
console.log('🔍 DEBUG: createVideoElement called for:', video.name);
|
||||
const duration = this.formatDuration(video.duration);
|
||||
const fileSize = this.formatFileSize(video.size);
|
||||
const isSelected = this.selectedVideos.has(video.path);
|
||||
|
|
@ -409,23 +410,23 @@ class VideoLibrary {
|
|||
|
||||
if (this.currentView === 'grid') {
|
||||
return `
|
||||
<div class="video-card ${selectionClass}" data-video-path="${video.path}">
|
||||
<div class="video-card ${selectionClass}" data-video-path="${video.path}" style="background: red !important; border: 3px solid lime !important; height: auto !important; padding: 0 !important; margin: 0 !important; min-height: unset !important; overflow: hidden; border-radius: 10px;">
|
||||
${this.isSelectMode ? `<div class="video-selection-checkbox ${isSelected ? 'checked' : ''}">✓</div>` : ''}
|
||||
<div class="video-thumbnail" data-video-path="${video.path}">
|
||||
<div class="video-thumbnail" data-video-path="${video.path}" style="height: 80px !important; width: 100% !important; position: relative; overflow: hidden;">
|
||||
${thumbnailSrc ?
|
||||
`<img src="${thumbnailSrc}" alt="${video.name}" onload="this.style.opacity=1" style="opacity:0; transition: opacity 0.3s;">` :
|
||||
`<div class="thumbnail-placeholder" data-video-path="${video.path}">🎬</div>`
|
||||
`<img src="${thumbnailSrc}" alt="${video.name}" onload="this.style.opacity=1" style="opacity:0; transition: opacity 0.3s; width: 100%; height: 100%; object-fit: cover;">` :
|
||||
`<div class="thumbnail-placeholder" data-video-path="${video.path}" style="height: 100%; display: flex; align-items: center; justify-content: center;">🎬</div>`
|
||||
}
|
||||
<div class="video-duration">${duration}</div>
|
||||
<div class="video-duration" style="position: absolute; bottom: 5px; right: 5px; background: rgba(0,0,0,0.8); color: white; padding: 2px 6px; border-radius: 4px; font-size: 0.8rem;">${duration}</div>
|
||||
</div>
|
||||
<div class="video-details">
|
||||
<div class="video-name" title="${video.name}">${video.name}</div>
|
||||
<div class="video-meta">
|
||||
<div class="video-details" style="padding: 4px 8px;">
|
||||
<div class="video-name" title="${video.name}" style="font-weight: bold; margin-bottom: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 0.9rem;">${video.name}</div>
|
||||
<div class="video-meta" style="color: #999; font-size: 0.75rem; display: flex; justify-content: space-between;">
|
||||
<span>${video.resolution}</span>
|
||||
<span>${fileSize}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="video-actions">
|
||||
<div class="video-actions" style="padding: 0 8px 4px; display: flex; justify-content: space-between; opacity: 0; transition: opacity 0.3s ease;">
|
||||
<button class="btn btn-mini play-video" title="Play">▶</button>
|
||||
<button class="btn btn-mini add-to-playlist" title="Add to Playlist">➕</button>
|
||||
</div>
|
||||
|
|
@ -545,8 +546,14 @@ class VideoLibrary {
|
|||
}
|
||||
}
|
||||
|
||||
refreshLibrary() {
|
||||
refreshLibrary(clearThumbnailCache = false) {
|
||||
console.log('🔄 Refreshing video library...');
|
||||
|
||||
if (clearThumbnailCache) {
|
||||
console.log('🗑️ Clearing thumbnail cache...');
|
||||
this.clearThumbnailCache();
|
||||
}
|
||||
|
||||
this.videos = [];
|
||||
this.filteredVideos = [];
|
||||
this.selectedVideo = null;
|
||||
|
|
@ -556,6 +563,7 @@ class VideoLibrary {
|
|||
<div class="library-loading">
|
||||
<div class="loading-spinner"></div>
|
||||
<p>Refreshing video library...</p>
|
||||
${clearThumbnailCache ? '<p><small>Clearing thumbnail cache...</small></p>' : ''}
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
|
@ -564,6 +572,24 @@ class VideoLibrary {
|
|||
this.loadVideoLibrary();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
clearThumbnailCache() {
|
||||
// Find all thumbnail-related localStorage keys
|
||||
const keysToRemove = [];
|
||||
for (let i = 0; i < localStorage.length; i++) {
|
||||
const key = localStorage.key(i);
|
||||
if (key && (key.startsWith('thumbnail_') || key.startsWith('thumbnail_version_') || key.startsWith('thumbnail_failed_'))) {
|
||||
keysToRemove.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove thumbnail cache keys
|
||||
keysToRemove.forEach(key => {
|
||||
localStorage.removeItem(key);
|
||||
});
|
||||
|
||||
console.log(`🗑️ Cleared ${keysToRemove.length} thumbnail cache entries`);
|
||||
}
|
||||
|
||||
displayEmptyLibrary(message) {
|
||||
this.libraryContent.innerHTML = `
|
||||
|
|
@ -706,8 +732,25 @@ class VideoLibrary {
|
|||
localStorage.removeItem(cacheVersionKey);
|
||||
}
|
||||
|
||||
// Check if we've already tried and failed to generate a thumbnail
|
||||
const failureKey = `thumbnail_failed_${video.path}`;
|
||||
const lastFailure = localStorage.getItem(failureKey);
|
||||
const currentTime = Date.now();
|
||||
|
||||
// If we failed recently (within 1 hour), use fallback immediately
|
||||
if (lastFailure && (currentTime - parseInt(lastFailure)) < 3600000) {
|
||||
console.log(`📸 Using fallback thumbnail for ${video.name} (recent failure)`);
|
||||
const fallbackThumbnail = this.createFallbackThumbnail(video);
|
||||
this.updateThumbnailDisplay(video.path, fallbackThumbnail);
|
||||
return fallbackThumbnail.dataUrl;
|
||||
}
|
||||
|
||||
// Generate thumbnail asynchronously
|
||||
this.generateThumbnail(video);
|
||||
this.generateThumbnail(video).catch(error => {
|
||||
// Mark this video as failed for thumbnail generation
|
||||
localStorage.setItem(failureKey, currentTime.toString());
|
||||
console.warn(`📸 Thumbnail generation failed for ${video.name}, marked for fallback`);
|
||||
});
|
||||
|
||||
return null; // Will show placeholder initially
|
||||
}
|
||||
|
|
@ -796,7 +839,13 @@ class VideoLibrary {
|
|||
});
|
||||
|
||||
tempVideo.addEventListener('error', (e) => {
|
||||
console.warn(`⚠️ Failed to generate thumbnail for ${video.name}:`, e.message);
|
||||
document.body.removeChild(tempVideo);
|
||||
|
||||
// Create a fallback thumbnail placeholder that looks better
|
||||
const fallbackThumbnail = this.createFallbackThumbnail(video);
|
||||
this.updateThumbnailDisplay(video.path, fallbackThumbnail);
|
||||
|
||||
reject(new Error(`Failed to load video for thumbnail: ${e.message}`));
|
||||
});
|
||||
|
||||
|
|
@ -816,6 +865,54 @@ class VideoLibrary {
|
|||
}
|
||||
}
|
||||
|
||||
createFallbackThumbnail(video) {
|
||||
// Create a stylized fallback thumbnail using canvas
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
canvas.width = 320;
|
||||
canvas.height = 180;
|
||||
|
||||
// Create gradient background
|
||||
const gradient = ctx.createLinearGradient(0, 0, 320, 180);
|
||||
gradient.addColorStop(0, '#1e293b');
|
||||
gradient.addColorStop(0.5, '#334155');
|
||||
gradient.addColorStop(1, '#475569');
|
||||
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.fillRect(0, 0, 320, 180);
|
||||
|
||||
// Add play icon
|
||||
ctx.fillStyle = '#e2e8f0';
|
||||
ctx.font = 'bold 48px Arial';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText('▶', 160, 90);
|
||||
|
||||
// Add video name (truncated)
|
||||
ctx.fillStyle = '#cbd5e1';
|
||||
ctx.font = '14px Arial';
|
||||
ctx.textAlign = 'center';
|
||||
const truncatedName = video.name.length > 30 ? video.name.substring(0, 27) + '...' : video.name;
|
||||
ctx.fillText(truncatedName, 160, 140);
|
||||
|
||||
// Add "Video" label
|
||||
ctx.fillStyle = '#94a3b8';
|
||||
ctx.font = '12px Arial';
|
||||
ctx.fillText('Video File', 160, 160);
|
||||
|
||||
const fallbackDataUrl = canvas.toDataURL('image/png', 0.8);
|
||||
|
||||
return {
|
||||
dataUrl: fallbackDataUrl,
|
||||
width: 320,
|
||||
height: 180,
|
||||
aspectRatio: 16/9,
|
||||
isPortrait: false,
|
||||
isFallback: true
|
||||
};
|
||||
}
|
||||
|
||||
updateThumbnailDisplay(videoPath, thumbnailData) {
|
||||
// Handle both old (string) and new (object) thumbnail data formats
|
||||
const thumbnailSrc = typeof thumbnailData === 'string' ? thumbnailData : thumbnailData.dataUrl;
|
||||
|
|
|
|||
|
|
@ -2810,6 +2810,177 @@ class InteractiveTaskManager {
|
|||
return true; // Mirror tasks are completed when webcam mirror is closed
|
||||
}
|
||||
|
||||
/**
|
||||
* Create pose verification task
|
||||
*/
|
||||
async createPoseVerificationTask(task, container) {
|
||||
console.log('🔍 Creating pose verification task');
|
||||
|
||||
// Extract pose information from task
|
||||
const poseInstructions = this.extractPoseInstructions(task);
|
||||
const verificationDuration = task.verificationDuration || task.duration || 30;
|
||||
|
||||
// Create pose verification interface
|
||||
container.innerHTML = `
|
||||
<div class="pose-verification-container">
|
||||
<div class="pose-verification-header">
|
||||
<h3>🔍 Pose Verification Required</h3>
|
||||
<p>${task.story || 'Assume the required pose and verify with webcam'}</p>
|
||||
</div>
|
||||
<div class="pose-verification-content">
|
||||
<div class="pose-instructions">
|
||||
<h4>Required Pose:</h4>
|
||||
<div class="pose-description">${poseInstructions}</div>
|
||||
</div>
|
||||
<div class="verification-info">
|
||||
<p>🔍 Use your webcam to verify you're maintaining the correct pose</p>
|
||||
<p>⏱️ Hold the position for ${verificationDuration} seconds</p>
|
||||
</div>
|
||||
<button id="start-pose-verification-btn" class="btn btn-primary pose-verification-btn">
|
||||
🔍 Start Pose Verification
|
||||
</button>
|
||||
<div class="pose-verification-timer" id="pose-verification-timer" style="display: none;">
|
||||
<div class="verification-progress-container">
|
||||
<div class="progress-bar" id="pose-verification-progress-bar"></div>
|
||||
</div>
|
||||
<div class="timer-display" id="pose-verification-time">
|
||||
${verificationDuration}s
|
||||
</div>
|
||||
<div class="timer-label">Verification in progress...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Add event listener for the start button
|
||||
const startBtn = container.querySelector('#start-pose-verification-btn');
|
||||
|
||||
// Add hover effects
|
||||
startBtn.addEventListener('mouseenter', () => {
|
||||
startBtn.style.transform = 'translateY(-2px) scale(1.05)';
|
||||
startBtn.style.boxShadow = '0 5px 15px rgba(231, 76, 60, 0.5)';
|
||||
});
|
||||
|
||||
startBtn.addEventListener('mouseleave', () => {
|
||||
startBtn.style.transform = 'translateY(0) scale(1)';
|
||||
startBtn.style.boxShadow = '0 3px 10px rgba(231, 76, 60, 0.3)';
|
||||
});
|
||||
|
||||
// Add click handler to start pose verification
|
||||
startBtn.addEventListener('click', () => {
|
||||
if (this.game.webcamManager) {
|
||||
const verificationData = {
|
||||
instructions: `Assume the required pose: ${poseInstructions}`,
|
||||
verificationInstructions: poseInstructions,
|
||||
verificationDuration: verificationDuration,
|
||||
verificationText: task.actionText || task.story,
|
||||
task: task
|
||||
};
|
||||
|
||||
// Start verification mode
|
||||
this.game.webcamManager.startVerificationMode(verificationData);
|
||||
|
||||
// Start the timer and progress bar
|
||||
this.startPoseVerificationTimer(task, container);
|
||||
|
||||
} else {
|
||||
console.warn('Webcam not available - falling back to regular task completion');
|
||||
if (this.game && this.game.showNotification) {
|
||||
this.game.showNotification('Webcam not available. Task completed automatically.', 'warning');
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.completePoseVerificationTask(task);
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract pose instructions from task data
|
||||
*/
|
||||
extractPoseInstructions(task) {
|
||||
// Check for specific pose indicators in task text
|
||||
const text = `${task.story || ''} ${task.actionText || ''}`.toLowerCase();
|
||||
|
||||
if (text.includes('all fours') || text.includes('hands and knees')) {
|
||||
return 'Get on all fours with hands and knees on the ground';
|
||||
} else if (text.includes('kneel') || text.includes('kneeling')) {
|
||||
return 'Kneel with proper posture, hands on thighs';
|
||||
} else if (text.includes('present') || text.includes('spread')) {
|
||||
return 'Assume a presenting position as described';
|
||||
} else if (text.includes('position') && text.includes('stress')) {
|
||||
return 'Maintain stress position - hands behind head, standing on toes';
|
||||
} else if (text.includes('crawl')) {
|
||||
return 'Get into crawling position on hands and knees';
|
||||
} else if (text.includes('shameful') && text.includes('position')) {
|
||||
return 'Assume the shameful position as described in the task';
|
||||
} else {
|
||||
return task.actionText || 'Assume the position described in the task';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start pose verification timer
|
||||
*/
|
||||
startPoseVerificationTimer(task, container) {
|
||||
const duration = task.verificationDuration || task.duration || 30;
|
||||
const timerSection = container.querySelector('#pose-verification-timer');
|
||||
const progressBar = container.querySelector('#pose-verification-progress-bar');
|
||||
const timeDisplay = container.querySelector('#pose-verification-time');
|
||||
const startBtn = container.querySelector('#start-pose-verification-btn');
|
||||
|
||||
// Show timer and hide start button
|
||||
timerSection.style.display = 'block';
|
||||
startBtn.style.display = 'none';
|
||||
|
||||
let timeLeft = duration;
|
||||
const interval = setInterval(() => {
|
||||
timeLeft--;
|
||||
timeDisplay.textContent = `${timeLeft}s`;
|
||||
|
||||
const progress = ((duration - timeLeft) / duration) * 100;
|
||||
progressBar.style.width = `${progress}%`;
|
||||
|
||||
if (timeLeft <= 0) {
|
||||
clearInterval(interval);
|
||||
this.completePoseVerificationTask(task);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
// Store interval for cleanup
|
||||
this.poseVerificationInterval = interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete pose verification task
|
||||
*/
|
||||
completePoseVerificationTask(task) {
|
||||
console.log('✅ Pose verification task completed');
|
||||
|
||||
// Clean up interval
|
||||
if (this.poseVerificationInterval) {
|
||||
clearInterval(this.poseVerificationInterval);
|
||||
this.poseVerificationInterval = null;
|
||||
}
|
||||
|
||||
// Close webcam verification if active
|
||||
if (this.game.webcamManager) {
|
||||
this.game.webcamManager.stopCamera();
|
||||
}
|
||||
|
||||
// Complete the task
|
||||
this.completeInteractiveTask();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate pose verification task
|
||||
*/
|
||||
async validatePoseVerificationTask(task) {
|
||||
return true; // Pose verification tasks are completed when timer expires
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a scenario step involves photography
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1463,7 +1463,7 @@ body.cinema-mode {
|
|||
.library-grid {
|
||||
display: grid !important;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)) !important;
|
||||
gap: 15px !important;
|
||||
gap: 8px !important;
|
||||
visibility: visible !important;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
|
@ -1480,7 +1480,7 @@ body.cinema-mode {
|
|||
visibility: visible !important;
|
||||
opacity: 1 !important;
|
||||
height: auto !important;
|
||||
min-height: 200px !important;
|
||||
min-height: unset !important;
|
||||
}
|
||||
|
||||
.video-card:hover {
|
||||
|
|
@ -1532,7 +1532,7 @@ body.cinema-mode {
|
|||
|
||||
.video-thumbnail {
|
||||
width: 100%;
|
||||
height: 120px;
|
||||
height: 80px;
|
||||
background: #333;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -1567,26 +1567,27 @@ body.cinema-mode {
|
|||
}
|
||||
|
||||
.video-details {
|
||||
padding: 12px;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
.video-name {
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
margin-bottom: 2px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.video-meta {
|
||||
color: #999;
|
||||
font-size: 0.85rem;
|
||||
font-size: 0.75rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.video-actions {
|
||||
padding: 0 12px 12px;
|
||||
padding: 0 8px 4px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
opacity: 0;
|
||||
|
|
|
|||
|
|
@ -2800,12 +2800,13 @@ body.theme-monochrome {
|
|||
/* Video Gallery Styles */
|
||||
#lib-video-gallery {
|
||||
display: grid !important;
|
||||
min-height: 80vh !important;
|
||||
min-height: 200vh !important;
|
||||
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)) !important; /* Reduced from 180px for more columns */
|
||||
gap: 8px !important; /* Reduced gap for tighter layout */
|
||||
max-height: 80vh !important;
|
||||
gap: 4px !important; /* Small gap for better visual balance */
|
||||
grid-auto-rows: auto !important; /* Override the generic .video-gallery minmax setting */
|
||||
max-height: 200vh !important;
|
||||
overflow-y: auto !important;
|
||||
padding: var(--space-md) !important;
|
||||
padding:10px !important; /* Reduced padding */
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
background: var(--bg-secondary);
|
||||
|
|
@ -2838,7 +2839,7 @@ body.theme-monochrome {
|
|||
/* Compact video thumbnail container */
|
||||
#lib-video-gallery .video-thumbnail-container {
|
||||
width: 100% !important;
|
||||
height: 50px !important; /* Smaller but still visible */
|
||||
height: 80px !important; /* Compact but visible */
|
||||
border-radius: 0 !important;
|
||||
overflow: hidden !important;
|
||||
background: var(--bg-secondary) !important;
|
||||
|
|
@ -2852,7 +2853,7 @@ body.theme-monochrome {
|
|||
/* Compact video thumbnail */
|
||||
#lib-video-gallery .video-thumbnail {
|
||||
width: 100% !important;
|
||||
height: 50px !important; /* Match container height */
|
||||
height: 80px !important; /* Match container height */
|
||||
object-fit: cover !important;
|
||||
display: block !important;
|
||||
border-radius: 0 !important;
|
||||
|
|
@ -2860,38 +2861,39 @@ body.theme-monochrome {
|
|||
|
||||
/* Compact video info section */
|
||||
#lib-video-gallery .video-info {
|
||||
padding: 4px 6px !important; /* Reasonable padding */
|
||||
background: var(--bg-card) !important;
|
||||
height: auto !important; /* Let content determine height */
|
||||
padding: 8px 6px !important; /* Increased padding for more space */
|
||||
background: rgba(0, 0, 0, 0.9) !important; /* Darker background for better contrast */
|
||||
height: 75px !important; /* Increased height for better text display */
|
||||
overflow: hidden !important;
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
justify-content: center !important;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.2) !important;
|
||||
}
|
||||
|
||||
#lib-video-gallery .video-name {
|
||||
font-size: 11px !important; /* Readable font size */
|
||||
color: var(--text-primary) !important;
|
||||
margin: 0 0 2px 0 !important; /* Small bottom margin */
|
||||
font-weight: 500 !important;
|
||||
line-height: 1.1 !important;
|
||||
font-size: 12px !important; /* Slightly larger font for better visibility */
|
||||
color: #ffffff !important; /* Bright white for visibility */
|
||||
margin: 0 0 3px 0 !important; /* Increased bottom margin */
|
||||
font-weight: 600 !important; /* Bolder font weight */
|
||||
line-height: 1.2 !important;
|
||||
overflow: hidden !important;
|
||||
text-overflow: ellipsis !important;
|
||||
white-space: nowrap !important;
|
||||
}
|
||||
|
||||
#lib-video-gallery .video-directory {
|
||||
font-size: 9px !important; /* Small but readable */
|
||||
color: var(--text-secondary) !important;
|
||||
opacity: 0.7 !important;
|
||||
font-size: 10px !important; /* Slightly larger for better readability */
|
||||
color: #e0e0e0 !important; /* Brighter light gray for better visibility */
|
||||
opacity: 1 !important; /* Remove opacity to ensure visibility */
|
||||
overflow: hidden !important;
|
||||
text-overflow: ellipsis !important;
|
||||
white-space: nowrap !important;
|
||||
line-height: 1.0 !important;
|
||||
line-height: 1.1 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
/* Override conflicting gallery item styles for video items - MATCH IMAGE GALLERY EXACTLY */
|
||||
/* Override conflicting gallery item styles for video items - COMPACT LAYOUT */
|
||||
#lib-video-gallery .gallery-item.video-item {
|
||||
position: relative !important;
|
||||
background: var(--bg-card) !important;
|
||||
|
|
@ -2900,12 +2902,13 @@ body.theme-monochrome {
|
|||
border: 1px solid var(--border-color) !important;
|
||||
transition: all 0.3s ease !important;
|
||||
cursor: pointer !important;
|
||||
display: block !important; /* Match image items */
|
||||
width: auto !important; /* Let it size naturally like images */
|
||||
height: auto !important; /* Let it size naturally like images */
|
||||
min-height: auto !important; /* Remove forced height */
|
||||
max-height: none !important; /* Remove height restrictions */
|
||||
padding: 0 !important; /* Remove padding to match images */
|
||||
display: block !important;
|
||||
width: auto !important;
|
||||
height: 240px !important; /* Thumbnail (80px) + video info (40px) + padding (20px) */
|
||||
min-height: 240px !important;
|
||||
max-height: 240px !important; /* Accommodate thumbnail + info section */
|
||||
padding: 0 !important;
|
||||
margin: 0 !important; /* Ensure no margins */
|
||||
}
|
||||
|
||||
/* Video item hover effect - MATCH IMAGE GALLERY */
|
||||
|
|
@ -2918,13 +2921,13 @@ body.theme-monochrome {
|
|||
/* Ensure video thumbnail container is visible */
|
||||
#lib-video-gallery .video-item .video-thumbnail-container {
|
||||
display: block !important;
|
||||
width: 120px !important;
|
||||
height: 90px !important;
|
||||
width: 100% !important;
|
||||
height: 80px !important;
|
||||
background: var(--bg-secondary) !important;
|
||||
border: 1px solid var(--border-color) !important;
|
||||
border-radius: 8px !important;
|
||||
overflow: hidden !important;
|
||||
margin-bottom: 8px !important;
|
||||
margin-bottom: 4px !important;
|
||||
flex-shrink: 0 !important;
|
||||
opacity: 1 !important;
|
||||
visibility: visible !important;
|
||||
|
|
@ -3028,7 +3031,7 @@ body.theme-monochrome {
|
|||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
grid-auto-rows: minmax(320px, auto);
|
||||
gap: var(--space-lg);
|
||||
gap: 0px;
|
||||
padding: var(--space-lg);
|
||||
background: var(--bg-secondary);
|
||||
border-radius: 12px;
|
||||
|
|
@ -4192,8 +4195,8 @@ body.theme-monochrome {
|
|||
.video-grid-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 15px;
|
||||
padding: 15px;
|
||||
gap: 8px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.video-item {
|
||||
|
|
@ -6048,7 +6051,7 @@ button#start-mirror-btn:disabled {
|
|||
}
|
||||
|
||||
.gallery-item-info {
|
||||
padding: 10px;
|
||||
padding: 4px;
|
||||
background: var(--bg-secondary);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
|
@ -6071,10 +6074,10 @@ button#start-mirror-btn:disabled {
|
|||
|
||||
/* Audio and Video item styles */
|
||||
.audio-item, .video-item {
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 80px;
|
||||
padding: 0;
|
||||
display: block;
|
||||
align-items: unset;
|
||||
min-height: unset;
|
||||
}
|
||||
|
||||
/* Specific override for video items in the gallery - DISABLE CONFLICTING STYLES */
|
||||
|
|
@ -6281,7 +6284,7 @@ button#start-mirror-btn:disabled {
|
|||
|
||||
#unified-video-gallery .video-thumbnail-container {
|
||||
width: 100% !important;
|
||||
height: 120px !important; /* Reduced from 150px for more compact layout */
|
||||
height: 80px !important; /* Much more compact */
|
||||
border-radius: 0 !important;
|
||||
overflow: hidden !important;
|
||||
background: var(--bg-secondary) !important;
|
||||
|
|
@ -6294,14 +6297,14 @@ button#start-mirror-btn:disabled {
|
|||
|
||||
#unified-video-gallery .video-thumbnail {
|
||||
width: 100% !important;
|
||||
height: 120px !important; /* Reduced from 150px */
|
||||
height: 80px !important; /* Match container height */
|
||||
object-fit: cover !important;
|
||||
display: block !important;
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
|
||||
#unified-video-gallery .video-info {
|
||||
padding: 8px !important; /* Reduced padding from 10px */
|
||||
padding: 3px 4px !important; /* Minimal padding */
|
||||
background: var(--bg-card) !important;
|
||||
}
|
||||
|
||||
|
|
@ -6325,6 +6328,35 @@ button#start-mirror-btn:disabled {
|
|||
white-space: nowrap !important;
|
||||
}
|
||||
|
||||
/* AGGRESSIVE VIDEO ITEM COMPACTNESS OVERRIDES */
|
||||
#lib-video-gallery .video-item,
|
||||
#unified-video-gallery .video-item,
|
||||
.video-item {
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
min-height: unset !important;
|
||||
height: auto !important;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
#lib-video-gallery .gallery-item.video-item,
|
||||
#unified-video-gallery .gallery-item.video-item,
|
||||
.gallery-item.video-item {
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
min-height: unset !important;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
/* Remove any extra spacing from gallery item info in video items */
|
||||
#lib-video-gallery .gallery-item-info,
|
||||
#unified-video-gallery .gallery-item-info,
|
||||
.video-item .gallery-item-info {
|
||||
padding: 2px 4px !important;
|
||||
margin: 0 !important;
|
||||
min-height: unset !important;
|
||||
}
|
||||
|
||||
.video-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
|
@ -6827,6 +6859,8 @@ button#start-mirror-btn:disabled {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.video-billboard-container {
|
||||
width: 380px;
|
||||
|
|
|
|||
|
|
@ -674,9 +674,9 @@
|
|||
|
||||
<!-- Required game buttons -->
|
||||
<button id="complete-btn" style="display: none;">Complete Task</button>
|
||||
<button id="skip-btn" style="display: none;">Skip Task</button>
|
||||
<button id="skip-btn" style="display: none;">Quit Training</button>
|
||||
<button id="pause-btn" style="display: none;">Pause</button>
|
||||
<button id="quit-btn" style="display: none;">Quit</button>
|
||||
<button id="quit-btn" style="display: none;">Quit Training</button>
|
||||
<button id="play-again-btn" style="display: none;">Play Again</button>
|
||||
|
||||
<!-- Game status elements -->
|
||||
|
|
@ -1966,7 +1966,7 @@
|
|||
`).join('')}
|
||||
</div>
|
||||
<div class="training-controls">
|
||||
<button onclick="loadNextTrainingTask()" class="next-btn">Skip Scenario</button>
|
||||
<button onclick="loadNextTrainingTask()" class="next-btn">Quit Scenario</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
@ -1979,7 +1979,7 @@
|
|||
</div>
|
||||
<div class="training-controls">
|
||||
<button onclick="selectScenarioChoice('${step.nextStep}')" class="action-btn">Continue</button>
|
||||
<button onclick="loadNextTrainingTask()" class="next-btn">Skip Scenario</button>
|
||||
<button onclick="loadNextTrainingTask()" class="next-btn">Quit Training</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
@ -2001,7 +2001,7 @@
|
|||
</div>
|
||||
<div class="training-controls">
|
||||
<button onclick="startMirrorAction('${step.nextStep}', ${step.duration})" class="action-btn">Start Mirror Task</button>
|
||||
<button onclick="selectScenarioChoice('${step.nextStep}')" class="skip-btn">Skip</button>
|
||||
<button onclick="selectScenarioChoice('${step.nextStep}')" class="skip-btn">Quit Training</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
@ -2056,7 +2056,7 @@
|
|||
<button id="complete-action-btn" onclick="completeScenarioAction('${step.nextStep}')" class="complete-btn" disabled>
|
||||
Action Complete (<span id="btn-timer">${Math.floor(duration/60)}:${(duration%60).toString().padStart(2,'0')}</span>)
|
||||
</button>
|
||||
<button onclick="selectScenarioChoice('${step.nextStep}')" class="next-btn">Skip to Next</button>
|
||||
<button onclick="selectScenarioChoice('${step.nextStep}')" class="next-btn">Quit Training</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
@ -2099,7 +2099,7 @@
|
|||
</div>
|
||||
<div class="training-controls">
|
||||
<button onclick="startPositionVerification('${step.nextStep}', ${step.verificationDuration}, '${step.verificationInstructions}', '${step.verificationText}')" class="action-btn">🔍 Start Position Verification</button>
|
||||
<button onclick="selectScenarioChoice('${step.nextStep}')" class="skip-btn">Skip (Failure)</button>
|
||||
<button onclick="selectScenarioChoice('${step.nextStep}')" class="skip-btn">Quit Training</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
@ -2472,7 +2472,7 @@
|
|||
</div>
|
||||
<div class="training-controls">
|
||||
<button onclick="startSimpleFocusSession(${task.duration || 60})" class="complete-btn">Start Focus Session</button>
|
||||
<button onclick="loadNextTrainingTask()" class="next-btn">Skip Task</button>
|
||||
<button onclick="loadNextTrainingTask()" class="next-btn">Quit Training</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
Loading…
Reference in New Issue