Tuan Nguyen 11 luni în urmă
părinte
comite
7aa051175a

+ 1 - 1
modules/connection/connection_manager.py

@@ -345,7 +345,7 @@ def update_machine_position():
         logger.info('Saving machine position')
         state.machine_x, state.machine_y = get_machine_position()
         state.save()
-        logger.info(f'Machine position: {state.machine_x}, {state.machine_y}')
+        logger.info(f'Machine position saved: {state.machine_x}, {state.machine_y}')
     
 def restart_connection(homing=False):
     """

+ 19 - 4
modules/core/pattern_manager.py

@@ -348,9 +348,20 @@ def get_status():
         "current_file": state.current_playing_file,
         "is_paused": state.pause_requested,
         "is_running": bool(state.current_playing_file and not state.stop_requested),
-        "progress": None
+        "progress": None,
+        "playlist": None
     }
     
+    # Add playlist information if available
+    if state.current_playlist and state.current_playlist_index is not None:
+        next_index = state.current_playlist_index + 1
+        status["playlist"] = {
+            "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)
+        }
+    
     if state.execution_progress:
         current, total, remaining_time, elapsed_time = state.execution_progress
         status["progress"] = {
@@ -374,14 +385,18 @@ async def broadcast_progress():
         status = get_status()
         disconnected = set()
         
-        for websocket in active_status_connections:
+        # Create a copy of the set for iteration
+        active_connections = active_status_connections.copy()
+        
+        for websocket in active_connections:
             try:
                 await websocket.send_json(status)
             except Exception:
                 disconnected.add(websocket)
         
         # Clean up disconnected clients
-        active_status_connections.difference_update(disconnected)
+        if disconnected:
+            active_status_connections.difference_update(disconnected)
         
         # Wait before next update
-        await asyncio.sleep(0.5)
+        await asyncio.sleep(1)

+ 1 - 1
modules/core/playlist_manager.py

@@ -105,7 +105,7 @@ async def run_playlist(playlist_name, pause_time=0, clear_pattern=None, run_mode
 
     try:
         logger.info(f"Starting playlist '{playlist_name}' with mode={run_mode}, shuffle={shuffle}")
-        state.current_playlist = playlist_name
+        state.current_playlist = file_paths
         asyncio.create_task(
             pattern_manager.run_theta_rho_files(
                 file_paths,

+ 34 - 1
static/css/style.css

@@ -910,9 +910,16 @@ body.playing .bottom-nav {
 }
 
 #currently-playing-container {
+    display: none; /* Hide by default */
     align-items: center;
     background: var(--background-accent);
     color: var(--text-secondary);
+    transition: all var(--transition-medium);
+    position: fixed;
+    bottom: 60px;
+    left: 0;
+    right: 0;
+    z-index: 1000;
 }
 
 #currently-playing-container h3,
@@ -921,12 +928,29 @@ body.playing .bottom-nav {
     color: var(--text-secondary);
 }
 
+#currently-playing-container .file-info {
+    display: flex;
+    flex-direction: column;
+    align-items: flex-start;
+    margin-right: auto;
+}
+
 #currently-playing-container h3 {
     margin: 0;
 }
 
+#currently-playing-container h4 {
+    margin: 0;
+    color: var(--text-secondary);
+    opacity: 0.7;
+    text-align: left;
+    font-size: 0.9em;
+    font-weight: normal;
+}
+
 body:not(.playing) #currently-playing-container {
     display: none;
+    opacity: 0;
 }
 
 #currently-playing-container.open {
@@ -935,11 +959,13 @@ body:not(.playing) #currently-playing-container {
 }
 
 body.playing #currently-playing-container:not(.open) {
+    display: flex;
     height: 140px;
-    overflow:hidden;
+    overflow: hidden;
     flex-direction: row;
     flex-wrap: wrap;
     bottom: 60px;
+    opacity: 1;
 }
 
 body.playing #currently-playing-container .header{
@@ -968,11 +994,18 @@ body.playing #currently-playing-container:not(.open) .header h3 {
 body.playing #currently-playing-container:not(.open) #currently-playing-details{
     flex-grow: 1;
     flex-basis: 50%;
+    display: flex;
+    flex-direction: column;
     align-items: flex-start;
     margin: 0 0 0 10px;
     overflow-y: auto;
 }
 
+body.playing #currently-playing-container:not(.open) #currently-playing-details .play-buttons {
+    margin-top: 10px;
+    align-self: flex-start;
+}
+
 body.playing #currently-playing-container:not(.open) .play-buttons button {
     width: 50px;
     height: 50px;

+ 91 - 63
static/js/main.js

@@ -1696,15 +1696,18 @@ themeIcon.className = savedTheme === 'dark' ? 'fas fa-sun' : 'fas fa-moon';
 
 // Add WebSocket connection for status updates
 let statusSocket = null;
-
 let reconnectAttempts = 0;
 const MAX_RECONNECT_ATTEMPTS = 5;
+let statusUpdateInterval = null;
 
 function connectStatusWebSocket() {
-    // Close existing connection if any
+    // Close existing connection and clear interval if any
     if (statusSocket) {
         statusSocket.close();
     }
+    if (statusUpdateInterval) {
+        clearInterval(statusUpdateInterval);
+    }
 
     // Create WebSocket connection
     const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
@@ -1713,39 +1716,42 @@ function connectStatusWebSocket() {
     statusSocket.onopen = () => {
         console.log('Status WebSocket connected');
         reconnectAttempts = 0; // Reset reconnect attempts on successful connection
-        // Request initial status
-        statusSocket.send('get_status');
         
-        // Request status updates periodically
-        setInterval(() => {
-            if (statusSocket.readyState === WebSocket.OPEN) {
+        // Immediately request initial status
+        if (statusSocket.readyState === WebSocket.OPEN) {
+            console.log('Requesting initial status...');
+            statusSocket.send('get_status');
+        }
+        
+        // Set up periodic status updates
+        statusUpdateInterval = setInterval(() => {
+            if (statusSocket && statusSocket.readyState === WebSocket.OPEN) {
                 statusSocket.send('get_status');
             }
-        }, 1000); // Request update every second
+        }, 1000);
     };
 
-    // Add debouncing for UI updates
-    let updateTimeout;
     statusSocket.onmessage = (event) => {
         try {
-            const status = JSON.parse(event.data);
-            console.log('Received status update:', status);
-            clearTimeout(updateTimeout);
-            updateTimeout = setTimeout(() => {
-                console.log('Updating UI with status:', status);
-                updateCurrentlyPlayingUI(status);
-            }, 100); // Debounce updates to 100ms
+            console.log('Status data received:', event.data);
+            const message = JSON.parse(event.data);
+            if (message.type === 'status_update' && message.data) {
+                updateCurrentlyPlayingUI(message.data);
+            }
         } catch (error) {
             console.error('Error processing status update:', error);
+            console.error('Raw data that caused error:', event.data);
         }
     };
 
     statusSocket.onclose = () => {
-        console.log('Status WebSocket disconnected.');
+        console.log('Status WebSocket disconnected');
+        clearInterval(statusUpdateInterval);
+        
         if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
             reconnectAttempts++;
-            const delay = Math.min(1000 * Math.pow(2, reconnectAttempts), 30000); // Exponential backoff, max 30 seconds
-            console.log(`Attempting to reconnect in ${delay/1000} seconds... (Attempt ${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})`);
+            const delay = Math.min(1000 * Math.pow(2, reconnectAttempts), 30000);
+            console.log(`Reconnecting in ${delay/1000}s (Attempt ${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})`);
             setTimeout(connectStatusWebSocket, delay);
         } else {
             console.error('Max reconnection attempts reached. Please refresh the page.');
@@ -1792,62 +1798,84 @@ const HIDE_DELAY = 5000; // 1 second delay before hiding
 let lastPlayedFile = null;
 
 function updateCurrentlyPlayingUI(status) {
-    const currentlyPlayingContainer = document.getElementById('currently-playing-container');
-    const currentlyPlayingFile = document.getElementById('currently-playing-file');
+    console.log('Updating UI with status:', status);
+
+    // Get all required DOM elements once
+    const container = document.getElementById('currently-playing-container');
+    const fileNameElement = document.getElementById('currently-playing-file');
     const progressBar = document.getElementById('play_progress');
     const progressText = document.getElementById('play_progress_text');
     const pausePlayButton = document.getElementById('pausePlayCurrent');
 
-    if (!currentlyPlayingContainer || !currentlyPlayingFile || !progressBar || !progressText) return;
+    // Check if all required elements exist
+    if (!container || !fileNameElement || !progressBar || !progressText) {
+        console.log('Required DOM elements not found:', {
+            container: !!container,
+            fileNameElement: !!fileNameElement,
+            progressBar: !!progressBar,
+            progressText: !!progressText
+        });
+        setTimeout(() => updateCurrentlyPlayingUI(status), 100);
+        return;
+    }
 
-    const now = Date.now();
-    
-    if (status.current_file) {
-        // Update last playing time
-        lastPlayingTime = now;
-        
-        // Show the container and update file name
+    // Update container visibility based on status
+    if (status.current_file && status.is_running) {
+        console.log('Pattern is running, showing container');
         document.body.classList.add('playing');
-        const fileName = status.current_file.replace('./patterns/', '');
-        currentlyPlayingFile.textContent = fileName;
-        
-        // Only update preview when a new pattern starts playing
-        if (lastPlayedFile !== status.current_file) {
-            lastPlayedFile = status.current_file;
-            // Remove the ./patterns/ prefix since previewPattern will add it
-            const cleanFileName = status.current_file.replace('./patterns/', '');
-            // Use the existing preview_thr endpoint
-            previewPattern(cleanFileName, 'currently-playing-container');
-        }
-        
-        // Update progress information if available
-        if (status.progress) {
-            const progress = status.progress;
-            const percentage = progress.percentage.toFixed(1);
-            const remainingTime = progress.remaining_time === null ? 'calculating...' : formatSecondsToHMS(progress.remaining_time);
-            const elapsedTime = formatSecondsToHMS(progress.elapsed_time);
-            
-            progressBar.value = percentage;
-            progressText.textContent = `${percentage}% (Elapsed: ${elapsedTime} | Remaining: ${remainingTime})`;
+        container.style.display = 'flex';
+    } else {
+        console.log('No pattern running, hiding container');
+        document.body.classList.remove('playing');
+        container.style.display = 'none';
+        return;
+    }
+
+    // Update file name display
+    const fileName = status.current_file.replace('./patterns/', '');
+    fileNameElement.textContent = fileName;
+
+    // Update next file display
+    const nextFileElement = document.getElementById('next-file');
+    if (nextFileElement) {
+        if (status.playlist && status.playlist.next_file) {
+            const nextFileName = status.playlist.next_file.replace('./patterns/', '');
+            nextFileElement.textContent = `(Next: ${nextFileName})`;
+            nextFileElement.style.display = 'block';
         } else {
-            progressBar.value = 0;
-            progressText.textContent = '0%';
+            nextFileElement.style.display = 'none';
         }
+    }
 
-        // Update pause/play button
-        if (pausePlayButton) {
-            pausePlayButton.innerHTML = status.is_paused ? 
-                '<i class="fa-solid fa-play"></i>' : 
-                '<i class="fa-solid fa-pause"></i>';
-        }
-    } else if (now - lastPlayingTime > HIDE_DELAY) {
-        // Only hide the container if we haven't had a playing file for more than HIDE_DELAY
-        document.body.classList.remove('playing');
+    // Update pattern preview if it's a new pattern
+    if (lastPlayedFile !== status.current_file) {
+        lastPlayedFile = status.current_file;
+        const cleanFileName = status.current_file.replace('./patterns/', '');
+        previewPattern(cleanFileName, 'currently-playing-container');
+    }
+
+    // Update progress information
+    if (status.progress) {
+        const { percentage, remaining_time, elapsed_time } = status.progress;
+        const formattedPercentage = percentage.toFixed(1);
+        const remainingText = remaining_time === null ? 'calculating...' : formatSecondsToHMS(remaining_time);
+        const elapsedText = formatSecondsToHMS(elapsed_time);
+
+        progressBar.value = formattedPercentage;
+        progressText.textContent = `${formattedPercentage}% (Elapsed: ${elapsedText} | Remaining: ${remainingText})`;
+    } else {
         progressBar.value = 0;
         progressText.textContent = '0%';
     }
 
-    // Update other UI elements based on status
+    // Update pause/play button if it exists
+    if (pausePlayButton) {
+        pausePlayButton.innerHTML = status.is_paused ? 
+            '<i class="fa-solid fa-play"></i>' : 
+            '<i class="fa-solid fa-pause"></i>';
+    }
+
+    // Update playlist UI if the function exists
     if (typeof updatePlaylistUI === 'function') {
         updatePlaylistUI(status);
     }

+ 4 - 1
templates/index.html

@@ -385,7 +385,10 @@
             <canvas id="currentlyPlayingCanvas"></canvas>
         </div>
         <div id="currently-playing-details">
-            <h3 id="currently-playing-file"></h3>
+            <div class="file-info">
+                <h3 id="currently-playing-file"></h3>
+                <h4 id="next-file"></h4>
+            </div>
             <p id="currently-playing-position"></p>
             <div class="play-buttons">
                 <button id="stopCurrent" onclick="stopExecution()" class="cancel">