Browser SLIDESHOW Remote Controlled

Place your projects here
Post Reply
User avatar
PeterN
Posts: 642
Joined: Mon Feb 08, 2021 7:56 pm
Location: Krefeld, Germany
Has thanked: 297 times
Been thanked: 360 times
Contact:

Browser SLIDESHOW Remote Controlled

Post by PeterN »

There was an idea from BasicBoy in another thread to use an old notebook as a slideshow photo frame. The slideshow should be controlled by a remote ESP32.
Out of curiosity, I tried to create the slideshow.html with the support of PERPLEXITY.AI. The script shows the JPG images in a local directory and reacts to commands sent from an ESP32 with ANNEX.
This example code responds with just an example sequence at http://<IP-ADR>/msg?x=1 or at HTTP://192.168.4.1/msg?x=1 if the ESP is in AP mode. However, it should be very easy to make it send commands according to local push buttons being pressed on the ESP32. I was just a bit too lazy to set that up, too ;-)

This is the SLIDESHOW.HTLM for a Chrome or Edge browser on the PC.
The script has to be stored locally on the PC to gain the necessary local file permissions.
It expects a local directory with foto001.jpg, foto002.jpg .... foto999.jpg
The IP-Address of the remote controlling ESP32 , the image directory and the number of files to show are set as constants in the code.
Please insert your values.

I did not find an easy way to examine the local directory for the actual original file names of my photos. So I took the way of renaming the image files by bulk-rename tool.

Use F11-key for fullscreen mode of the slideshow window.


Code: [Local Link Removed for Guests]


<!DOCTYPE html>
<html>
<head>
    <title>Slideshow</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
            background-color: black;
        }
        img {
            position: absolute;
            width: 100%;
            height: 100%;
            object-fit: contain;
            transition: opacity 2s;
        }
    </style>
</head>
<body>
    <script>
        // Check if the browser is Chrome or Edge
        const userAgent = navigator.userAgent;
        const isChromeOrEdge = userAgent.includes("Chrome") || userAgent.includes("Edg");

        // Display a warning if the browser is not Chrome or Edge
        if (!isChromeOrEdge) {
            alert("Warning: This application works best in Google Chrome or Microsoft Edge. Please switch to one of these browsers for optimal performance.");
        }

        const IMAGE_DIR = "C:/Users/pneuf/Pictures/XY/"; // Local directory for images
        const IMAGE_PREFIX = "foto"; // Prefix for image filenames
        const TOTAL_IMAGES = 60; // Total number of images
        // const SERVER_IP = "192.168.0.111"; // Server IP address
        const SERVER_IP = "192.168.0.145"; // Server IP address
        const CHECK_URL = `http://${SERVER_IP}/msg?x=1`; // URL to query for commands
        const COMMANDS = ["next", "previous", "repeat", "go", "stop"]; // Supported commands
        const CHANGE_INTERVAL = 30000; // Interval for changing slides (in milliseconds, 30 seconds)

        let currentIndex = 0; // Current image index (0-based)
        let interval;

        // Function to generate a list of image file names
        function generateImageList() {
            const images = [];
            for (let i = 1; i <= TOTAL_IMAGES; i++) {
                const paddedNumber = String(i).padStart(3, '0'); // Add leading zeros
                images.push(`${IMAGE_PREFIX}${paddedNumber}.jpg`);
            }
            return images;
        }

        const images = generateImageList();

        // Function to change the displayed image
        function changeImage(index) {
            const imgElement = document.createElement("img");
            imgElement.src = `${IMAGE_DIR}${images[index]}`;
            imgElement.style.opacity = 0;
            document.body.appendChild(imgElement);

            setTimeout(() => {
                imgElement.style.opacity = 1;
                const oldImages = document.querySelectorAll("img");
                if (oldImages.length > 1) {
                    document.body.removeChild(oldImages[0]);
                }
            }, 50);
        }

        // Function to start the slideshow
        function startSlideshow() {
            interval = setInterval(() => {
                currentIndex++;
                if (currentIndex >= images.length) {
                    currentIndex = 0; // Restart with the first image when reaching the upper limit
                }
                changeImage(currentIndex);
            }, CHANGE_INTERVAL); // Change image every 10 seconds
        }

        // Function to handle commands from the server or keyboard
        function handleCommand(command) {
            clearInterval(interval);

            if (!isNaN(command)) {
                // If the command is a number, show the corresponding image
                const requestedIndex = parseInt(command, 10) - 1; // Convert to zero-based index
                if (requestedIndex >= 0 && requestedIndex < images.length) {
                    currentIndex = requestedIndex;
                    changeImage(currentIndex);
                }
            } else if (command === "next") {
                currentIndex++;
                if (currentIndex >= images.length) {
                    currentIndex = 0; // Restart with the first image when reaching the upper limit
                }
                changeImage(currentIndex);
            } else if (command === "previous") {
                currentIndex--;
                if (currentIndex < 0) {
                    currentIndex = 0; // Stay at index 0 when trying to go below it
                }
                changeImage(currentIndex);
            } else if (command === "repeat") {
                changeImage(currentIndex); // Repeat the current image
            } else if (command === "go") {
                currentIndex = 0; // Go to the first image and restart slideshow
                changeImage(currentIndex);
                startSlideshow();
                return;
            } else if (command === "stop") {
                clearInterval(interval); // Stop the slideshow
                return; // Exit without restarting slideshow
            }

            startSlideshow();
        }

        // Function to periodically query the server for commands
        function checkServer() {
            setInterval(() => {
                fetch(CHECK_URL)
                    .then(response => response.text())
                    .then(text => {
                        text = text.trim(); // Remove any extra whitespace or newline characters

                        if (!isNaN(text)) {
                            handleCommand(text); // Handle numeric input as an image index
                        } else {
                            for (const command of COMMANDS) {
                                if (text.includes(command)) {
                                    handleCommand(command);
                                    break;
                                }
                            }
                        }
                    })
                    .catch(err => console.error("Error fetching URL:", err));
            }, 1000); // Check every 1 second
        }

        // Function to add keyboard event listeners for local control using arrow keys and p/n keys
        function addKeyboardControls() {
            document.addEventListener("keydown", (event) => {
                switch (event.key) { 
                    case 'ArrowRight': // Next image using right arrow key
                    case 'n':         // Next image using 'n' key
                        handleCommand("next");
                        break;
                    case 'ArrowLeft':  // Previous image using left arrow key
                    case 'p':          // Previous image using 'p' key
                        handleCommand("previous");
                        break;
                    case 'r':          // Repeat current image using 'r' key
                        handleCommand("repeat");
                        break;
                    case 'g':          // Restart slideshow from first image and go using 'g' key
                        handleCommand("go");
                        break;
                    case 's':          // Stop slideshow using 's' key
                        handleCommand("stop");
                        break;
                    default:
                        break; // Ignore other keys
                }
            });
        }

        // Initialization
        changeImage(currentIndex);
        startSlideshow();
        checkServer();
        addKeyboardControls(); // Add keyboard controls for local navigation using arrow keys and p/n keys
    </script>
</body>
</html>



THIS IS THE EXAMPLE ANNEX32-CODE FOR AN ESP32:

Code: [Local Link Removed for Guests]


' REMOTE.BAS  a remotecontrol for a browser based slideshow
' ----- PeterN 29/12/2024----------------------------------
' This script returns an example sequenze of commands
' to  a browser script "slideshow.html".
' slideshow.html displays local jpg-images and requests
' a controll command once per second.
' Commands are generated by this script at http://<IP-ADR>/msg?x=1
' This example send an endless sequenze.
' CMD$ may be set by a key button etc to remote controll the slideshow

cmd_go$      ="go"
cmd_play$    ="go"
cmd_stop$    ="stop"
cmd_next$    ="next"
cmd_previous$="previous"
cmd_repeat$  ="repeat"
onUrlMessage RETURN_CMD 

while 1       'endlessly responds with command sequence   
  cmd$=cmd_go$      'Restats slideshow with first pic
  pause 5000
  for i= 30 to 35
    cmd$ = str$(i)  'displays foto030.jpg to foto35.jpg
    pause 1200
  next i
  for i = 1 to 10
    cmd$=CMD_next$  'displays next pic
    pause 2000
  next i
  pause 10000       '10s Pause 
  wlog "10s Pause"
  for i = 1 to 3
    cmd$=CMD_previous$ 'one pic back
    pause 1000
  next i
  for i = 1 to 5
    cmd$=CMD_next$  'next pic
    pause 3000
  next i
WEND
END
WAIT

'---------------------------------------------------------
RETURN_CMD:
' Returns current CMD$  at http://<IP-ADR>/msg?x=1
' as next Command for requesting slideshow.html ;
' !BUT ONLY ONE TIME! Then will then return "NO_COMMAND"
UrlMsgReturn CMD$
wlog "Command returned is: " + CMD$
if CMD$ <> "NO_COMMAND" then  CMD$="NO_COMMAND"
return


And here some explanations of the slideshow.html generated with PERPLEXITY.AI

Code: [Local Link Removed for Guests]


Summary of Features:
	•	Slideshow Functionality: Displays images in fullscreen mode with smooth transitions every 10 seconds.
	•	Manual Navigation:
	•	Users can navigate through images using:
	•	ArrowRight or `n` for the next image.
	•	ArrowLeft or `p` for the previous image, which will not go below index `0`.
	•	`r` to repeat the current image.
	•	`g` to restart from the first image.
	•	`s` to stop the slideshow.
	•	Browser Detection: Alerts users if they are not using Google Chrome or Microsoft Edge.
	•	Server Communication: Queries a server every second for commands or numeric inputs.



The HTML code utilizes AJAX to respond to specific commands for controlling a slideshow. Here’s a summary of how it works:
	1.	AJAX Requests:
	•	The script periodically queries a server using the `fetch` API to receive commands or numeric inputs that dictate which image to display next. This is done every second.
	2.	Command Handling:
	•	When the server responds, the code checks if the response is a number (indicating a specific image index) or a command (like “next” or “previous”).
	•	Based on the response, it updates the `currentIndex` variable to point to the appropriate image in the array.
	3.	Image Transition:
	•	The `changeImage` function is called with the new index, which creates an image element, sets its source, and applies a fade-in effect.
	•	If the command is “next”, it increments the index and wraps around if it exceeds the total number of images.
	•	If the command is “previous”, it decrements the index and ensures it does not go below zero.
	4.	User Interaction:
	•	The script also listens for keyboard events (like pressing arrow keys or specific letters) to manually control the slideshow, allowing users to navigate through images without waiting for automatic transitions.
This combination of AJAX for real-time command handling and JavaScript for dynamic content updates creates an interactive slideshow experience.
User avatar
Basicboy
Posts: 179
Joined: Sat Jul 20, 2024 11:42 am
Location: Australia
Has thanked: 56 times
Been thanked: 13 times

Re: Browser SLIDESHOW Remote Controlled

Post by Basicboy »

Hi Peter
I've been meaning to come back to this post and thank you for it forever but for some reason never did. As they say, late better than never!

The program I wrote for the frame is in VB6. I actually wrote it over a year ago so I (very awkwardly) admit that I don't remember how I wrote it. I still have the code and will send it to you soon(ish) if you'd like to have a look and point out things I can improve

All I remember is that it goes to all subdirectories in a folder and indexes all images inside. Then, it shuffles the photos in a data file and starts displaying them. There are also two more database files for blocked pictures and for favorites (it has a favorite mode where only favorite pictures are displayed)

What I need next to make it "perfect" is two things:
1- find an AI program that analyses every picture and tells me if it is horizontal or vertical in orientation (based on the actual content of the picture)
2- get an ESP32 to physically rotate the entire frame using servo motor to display vertical and horizontal images.
I love this community!
User avatar
PeterN
Posts: 642
Joined: Mon Feb 08, 2021 7:56 pm
Location: Krefeld, Germany
Has thanked: 297 times
Been thanked: 360 times
Contact:

Re: Browser SLIDESHOW Remote Controlled

Post by PeterN »

Hi BasicBoy

If you want to use your „old“ program (and additionally make it respond to commands send from the ESP32) the AI of my trust told me this:

Summary of the Dialogue on JPG Orientation and the Script:
1. Question: Is the orientation of an image stored in JPG files?
• Answer: Yes, the orientation is stored in the EXIF metadata under the “Orientation” field. This field specifies how an image should be rotated or mirrored for correct display. Most image viewers and editors use this information automatically.
2. Question: Can EXIF information be extracted using a batch program?
• Answer: Yes, tools like ExifTool can extract EXIF metadata in batch mode. ExifTool is a powerful, free, and cross-platform command-line tool.
3. Request: Provide a batch script for Windows 11 that searches a directory for JPG files and saves their orientation in a `.ORI` file with the same base name as the original image.
• Solution: A batch script was provided that:
• Loops through all `.jpg` files in a specified directory.
• Uses ExifTool to extract the “Orientation” field.
• Saves the output to a `.ORI` file with the same base name as the corresponding image.

Code: [Local Link Removed for Guests]

@echo off
set "directory=YOUR_DIRECTORY_PATH"

:: Loop through all .jpg files in the specified directory
for %%f in ("%directory%\*.jpg") do (
    :: Extract orientation and save it to a .ORI file with the same base name
    exiftool -s -s -s -Orientation "%%f" > "%%~dpnf.ORI"
)

echo Done!
pause

Instructions for Use:
3. Replace `YOUR_DIRECTORY_PATH` with the path to your target folder.
2. Save the script as `extract_orientation.bat`.
3. Ensure ExifTool is installed and added to your system’s PATH.
4. Run the script to generate `.ORI` files containing orientation data for each JPG file in the directory.
This dialogue focused on understanding JPG metadata and automating its extraction using ExifTool.

But …sorry for my poor English … do you really want to ROTATE the heavy foto frame hardware accordingly to the orientation???? That is really hard coded!!:-)
Post Reply