Parcourir la source

add button to skip current running pattern

Tuan Nguyen il y a 11 mois
Parent
commit
42aa9ecb87

+ 6 - 0
app.py

@@ -529,6 +529,12 @@ async def get_wled_ip():
         raise HTTPException(status_code=404, detail="No WLED IP set")
     return {"success": True, "wled_ip": state.wled_ip}
 
+@app.post("/skip_pattern")
+async def skip_pattern():
+    if not state.current_playlist:
+        raise HTTPException(status_code=400, detail="No playlist is currently running")
+    state.skip_requested = True
+    return {"success": True}
 
 def signal_handler(signum, frame):
     """Handle shutdown signals gracefully but forcefully."""

+ 1 - 1
modules/connection/connection_manager.py

@@ -328,7 +328,7 @@ def check_idle():
             update_machine_position()
             return True
         time.sleep(1)
-
+        
 
 def get_machine_position(timeout=5):
     """

+ 64 - 37
modules/core/pattern_manager.py

@@ -140,7 +140,7 @@ def get_clear_pattern_file(clear_pattern_mode, path=None):
     """Return a .thr file path based on pattern_name."""
     if not clear_pattern_mode or clear_pattern_mode == 'none':
         return
-    logger.info("Clear pattern mode: " + clear_pattern_mode)
+    logger.debug("Clear pattern mode: " + clear_pattern_mode)
     if clear_pattern_mode == "random":
         return random.choice(list(CLEAR_PATTERNS.values()))
 
@@ -217,6 +217,13 @@ async def run_theta_rho_file(file_path, is_playlist=False):
                     if state.led_controller:
                         effect_idle(state.led_controller)
                     break
+                
+                if state.skip_requested:
+                    logger.info("Skipping pattern...")
+                    connection_manager.check_idle()
+                    if state.led_controller:
+                        effect_idle(state.led_controller)
+                    break
 
                 # Wait for resume if paused
                 if state.pause_requested:
@@ -264,8 +271,6 @@ async def run_theta_rho_file(file_path, is_playlist=False):
                 pass
             progress_update_task = None
             
-        if state.led_controller:
-            effect_idle(state.led_controller)
 
 async def run_theta_rho_files(file_paths, pause_time=0, clear_pattern=None, run_mode="single", shuffle=False):
     """Run multiple .thr files in sequence with options."""
@@ -274,65 +279,83 @@ async def run_theta_rho_files(file_paths, pause_time=0, clear_pattern=None, run_
     # Set initial playlist state
     state.playlist_mode = run_mode
     state.current_playlist_index = 0
-    state.current_playlist = file_paths
-    
     # Start progress update task for the playlist
     global progress_update_task
     if not progress_update_task:
         progress_update_task = asyncio.create_task(broadcast_progress())
     
+    
+    if shuffle:
+        random.shuffle(file_paths)
+        logger.info("Playlist shuffled")
+
+
     if shuffle:
         random.shuffle(file_paths)
         logger.info("Playlist shuffled")
 
     try:
         while True:
-            for idx, path in enumerate(file_paths):
-                logger.info(f"Upcoming pattern: {path}")
+            # Construct the complete pattern sequence
+            pattern_sequence = []
+            for path in file_paths:
+                # Add clear pattern if specified
+                if clear_pattern and clear_pattern != 'none':
+                    clear_file_path = get_clear_pattern_file(clear_pattern, path)
+                    if clear_file_path:
+                        pattern_sequence.append(clear_file_path)
+                
+                # Add main pattern
+                pattern_sequence.append(path)
+
+            # Shuffle if requested
+            if shuffle:
+                # Get pairs of patterns (clear + main) to keep them together
+                pairs = [pattern_sequence[i:i+2] for i in range(0, len(pattern_sequence), 2)]
+                random.shuffle(pairs)
+                # Flatten the pairs back into a single list
+                pattern_sequence = [pattern for pair in pairs for pattern in pair]
+                logger.info("Playlist shuffled")
+
+            # Set the playlist to the first pattern
+            state.current_playlist = pattern_sequence
+            # Execute the pattern sequence
+            for idx, file_path in enumerate(pattern_sequence):
                 state.current_playlist_index = idx
-
                 if state.stop_requested:
-                    logger.info("Execution stopped before starting next pattern")
+                    logger.info("Execution stopped")
                     return
 
-                if clear_pattern and clear_pattern != 'none':
-                    if state.stop_requested:
-                        logger.info("Execution stopped before running the next clear pattern")
-                        return
-
-                    clear_file_path = get_clear_pattern_file(clear_pattern, path)
-                    if clear_file_path:  # Only run clear pattern if we got a valid file path
-                        logger.info(f"Running clear pattern: {clear_file_path}")
-                        await run_theta_rho_file(clear_file_path, is_playlist=True)
-                    else:
-                        logger.info("Skipping clear pattern - no valid clear pattern file")
-
-                if not state.stop_requested:
-                    logger.info(f"Running pattern {idx + 1} of {len(file_paths)}: {path}")
-                    await run_theta_rho_file(path, is_playlist=True)
-
-                if idx < len(file_paths) - 1:
-                    if state.stop_requested:
-                        logger.info("Execution stopped before running the next clear pattern")
-                        return
-                    if pause_time > 0:
-                        logger.info(f"Pausing for {pause_time} seconds")
-                        await asyncio.sleep(pause_time)
+                # Update state for main patterns only
+                logger.info(f"Running pattern {file_path}")
+                
+                # Execute the pattern
+                await run_theta_rho_file(file_path, is_playlist=True)
+
+                # Handle pause between patterns
+                if idx < len(pattern_sequence) - 1 and not state.stop_requested and pause_time > 0 and not state.skip_requested:
+                    logger.info(f"Pausing for {pause_time} seconds")
+                    pause_start = time.time()
+                    while time.time() - pause_start < pause_time:
+                        if state.skip_requested:
+                            logger.info("Pause interrupted by stop/skip request")
+                            break
+                        await asyncio.sleep(1)  # Check every 100ms
+                    
+                state.skip_requested = False
 
             if run_mode == "indefinite":
                 logger.info("Playlist completed. Restarting as per 'indefinite' run mode")
                 if pause_time > 0:
                     logger.debug(f"Pausing for {pause_time} seconds before restarting")
                     await asyncio.sleep(pause_time)
-                if shuffle:
-                    random.shuffle(file_paths)
-                    logger.info("Playlist reshuffled for the next loop")
                 continue
             else:
                 logger.info("Playlist completed")
                 break
+
     finally:
-        # Clean up progress update task at the end of the playlist
+        # Clean up progress update task
         if progress_update_task:
             progress_update_task.cancel()
             try:
@@ -347,6 +370,10 @@ async def run_theta_rho_files(file_paths, pause_time=0, clear_pattern=None, run_
         state.current_playlist = None
         state.current_playlist_index = None
         state.playlist_mode = None
+        
+        if state.led_controller:
+            effect_idle(state.led_controller)
+        
         logger.info("All requested patterns completed (or stopped) and state cleared")
 
 def stop_actions(clear_playlist = True):
@@ -475,7 +502,7 @@ def get_status():
             "current_index": state.current_playlist_index,
             "total_files": len(state.current_playlist),
             "mode": state.playlist_mode,
-            "next_file": state.current_playlist[next_index] if next_index < len(state.current_playlist) else (state.current_playlist[0] if state.playlist_mode == "loop" else None)
+            "next_file": state.current_playlist[next_index] if next_index < len(state.current_playlist) else None
         }
     
     if state.execution_progress:

+ 1 - 0
modules/core/state.py

@@ -40,6 +40,7 @@ class AppState:
         self.port = None
         self.wled_ip = None
         self.led_controller = None
+        self.skip_requested = False
         self._playlist_mode = "loop"
         self._pause_time = 0
         self._clear_pattern = "none"

+ 1 - 6
static/css/style.css

@@ -957,10 +957,6 @@ body.playing #currently-playing-container .header .open-button {
     margin: 0;
 }
 
-body.playing #currently-playing-preview #currentlyPlayingCanvas {
-    max-width:100px;
-    padding: 5px;
-}
 
 body.playing #currently-playing-container:not(.open) .header .fullscreen-button,
 body.playing #currently-playing-container:not(.open) .header .close-button,
@@ -988,8 +984,7 @@ body.playing #currently-playing-container:not(.open) #progress-container {
 
 
 #currentlyPlayingCanvas {
-    width: 100%;
-    max-width: 300px;
+    width: 100px;
     aspect-ratio: 1/1;
     border: 1px solid var(--border-primary);
     background: var(--theme-secondary);

+ 36 - 0
static/js/main.js

@@ -1992,4 +1992,40 @@ function updateCurrentlyPlayingUI(status) {
     if (typeof updatePlaylistUI === 'function') {
         updatePlaylistUI(status);
     }
+
+    // Update skip button visibility based on playlist status
+    updateSkipButtonVisibility(status);
+}
+
+// Add these functions to handle the skip button
+
+function updateSkipButtonVisibility(status) {
+    const skipButton = document.getElementById('skipCurrent');
+    if (skipButton) {
+        // Check if a playlist is playing using the correct status properties
+        console.log('status', status);
+        const isPlaylistPlaying = status && status.playlist && status.playlist.next_file;
+        if (isPlaylistPlaying) {
+            skipButton.classList.remove('hidden');
+        } else {
+            skipButton.classList.add('hidden');
+        }
+    }
+}
+
+async function skipPattern() {
+    try {
+        const response = await fetch('/skip_pattern', {
+            method: 'POST',
+        });
+        const data = await response.json();
+        
+        if (data.success) {
+            logMessage('Skipping to next pattern', LOG_TYPE.INFO);
+        } else {
+            logMessage(data.message, LOG_TYPE.ERROR);
+        }
+    } catch (error) {
+        logMessage('Error skipping pattern', 'error');
+    }
 }

+ 3 - 0
templates/index.html

@@ -391,6 +391,9 @@
             </div>
             <p id="currently-playing-position"></p>
             <div class="play-buttons">
+                <button id="skipCurrent" onclick="skipPattern()" class="cta">
+                    <i class="fa-solid fa-step-forward"></i>
+                </button>
                 <button id="stopCurrent" onclick="stopExecution()" class="cancel">
                     <i class="fa-solid fa-stop"></i>
                 </button>