How I used Screenshot One and ChatGPT to automatically generate screenshots for this site

I took a two month break (about 62 days to be exact) and used that time to refocus and figure out what I wanted to do with this project. The first thing that came out of it was a pretty slick little automation for getting the screenshots. I think I initially did what most people do, and I tried to get too much out there. I should have just focused on the automation and making this as easy as possible to add sites. But it was super important to have the architecture right, and I have that. 

So given that this is little side project and things have been crazy with my companies, I decided to take a 2 month break from this and sort of plan where I wanted it to go. The constant screenshotting and dealing with cookie plugins and everything else was dragging on me. But this break got me thinking about automation and trying to make this as easy as possible on me.

Enter Screenshotone.com It seemed almost too good to be true. Every other screenshot tool just wasn’t working how I wanted it to, and entering one website could take up to 10 minutes, and that’s just too much time to spend per site on a side project. It needed to be as simple as dropping the URL in. I want this thing to have a thousand websites to browse. I mean I think with traffic hopefully comes with enough monetization to pay for the S3 costs, but we shall see.

I decided to start with just adding a simple button that I could click to generate screenshots and have it attach to the custom fields and to the featured image. That would save me the process of screenshotting, downloading, uploading, and all of that good stuff.

Here’s how I did it:

First, I created the button (in full disclosure, I used ChatGPT for some of it to save me even more time).

// Add the meta box with the screenshot button
function sc_add_screenshot_button_meta_box() {
    add_meta_box(
        'sc_screenshot_button',
        'Generate Screenshot',
        'sc_screenshot_button_callback',
        ['websites', 'screen'], // Add the button to both CPTs
        'side',
        'high'
    );
}
add_action('add_meta_boxes', 'sc_add_screenshot_button_meta_box');

This will put a button on my two post types. The site is made up of Websites CPT, and then Screen CPT that are related to the website cpt. Although this is due to change in a future update. (I’m getting rid of websites and just doing screens).

Next, I needed to render the button in the builder, and set it up to perform our intended actions

// Callback function to render the button in the meta box
function sc_screenshot_button_callback($post) {
    ?>
    <button type="button" class="button button-primary" id="sc-generate-screenshot">Generate Screenshot</button>
    <p id="sc-status"></p>
    <script type="text/javascript">
        jQuery(document).ready(function($) {
            $('#sc-generate-screenshot').on('click', function() {
                $('#sc-status').text('Generating screenshot...');
                var data = {
                    'action': 'sc_generate_screenshot',
                    'post_id': <?php echo $post->ID; ?>
                };

                $.post(ajaxurl, data, function(response) {
                    $('#sc-status').text(response);
                });
            });
        });
    </script>
    <?php
}

Then I needed to actually pull in the screenshot from ScreenshotOne (the code below is from the prior URLbox integration). I used their sandbox to play around with a tough screenshot (lots of scrolling animations and whatnot) to get it to work how I wanted it. Once I had those set, I could add the API key and make my API calls.

function sc_get_screenshot($url) {
    $api_key = 'YOUR API HERE'; // Your actual URLBox API key
    $base_url = "https://api.urlbox.io/v1/$api_key/png";
    
    // Options for the screenshot
    $options = array(
        'width' => 1440,
        'full_page' => 'true',
        'full_page_mode' => 'stitch',
        'block_ads' => 'true',
        'hide_cookie_banners' => 'true',
        'click_accept' => 'true',
        'url' => $url // The target URL
    );

    // Build the query string from the options
    $query_string = http_build_query($options);
    
    // Complete API URL
    $api_url = "$base_url?$query_string";

    // Fetch the image data
    $response = wp_remote_get($api_url, array('timeout' => 30)); // Adjust timeout as needed

    if (is_wp_error($response)) {
        return 'Error: ' . $response->get_error_message();
    }

    $image_data = wp_remote_retrieve_body($response);

    if (!empty($image_data)) {
        return $image_data;
    } else {
        return 'Error: Empty image data.';
    }
}

The final step was to save the image and attach it to the post.

function sc_generate_screenshot() {
    error_log('sc_generate_screenshot function was called.');

    if (!isset($_POST['post_id'])) {
        error_log('Missing post_id in AJAX request.');
        echo 'Invalid request. Missing post ID.';
        wp_die();
    }

    $post_id = intval($_POST['post_id']);
    $post_type = get_post_type($post_id);

    // Corrected error_log statement
    error_log('Processing screenshot generation for post ID: ' . $post_id . ' of type: ' . $post_type);

    if ($post_type == 'websites') {
        $url = get_post_meta($post_id, 'link_to_site', true);
    } elseif ($post_type == 'screen') {
        $url = get_post_meta($post_id, 'screen_url', true);
    } else {
        error_log('Invalid post type: ' . $post_type);
        echo 'Invalid post type.';
        wp_die();
    }

    if (!$url) {
        error_log('URL not found for post ID: ' . $post_id);
        echo 'URL not found.';
        wp_die();
    }

    $image_data = sc_get_screenshot($url);

    if (is_string($image_data) && strpos($image_data, 'Error:') !== false) {
        error_log('Error during screenshot generation: ' . $image_data);
        echo $image_data;
        wp_die();
    }

    if ($image_data) {
        $upload = wp_upload_bits("screenshot-$post_id.png", null, $image_data);

        if (!$upload['error']) {
            $filename = $upload['file'];

            $attachment = array(
                'post_mime_type' => 'image/png',
                'post_title'     => "Screenshot $post_id",
                'post_content'   => '',
                'post_status'    => 'inherit'
            );

            $attach_id = wp_insert_attachment($attachment, $filename, $post_id);

            require_once(ABSPATH . 'wp-admin/includes/image.php');
            $attach_data = wp_generate_attachment_metadata($attach_id, $filename);
            wp_update_attachment_metadata($attach_id, $attach_data);

            // Set as featured image
            set_post_thumbnail($post_id, $attach_id);

            if ($post_type == 'screen') {
                update_post_meta($post_id, 'screenshot', $attach_id);
            }

            echo 'Screenshot generated successfully!';
        } else {
            error_log('Error uploading screenshot: ' . $upload['error']);
            echo 'Error uploading screenshot: ' . $upload['error'];
        }
    } else {
        error_log('Error generating screenshot or empty response for post ID: ' . $post_id);
        echo 'Error generating screenshot or empty response.';
    }

    wp_die();
}

add_action('wp_ajax_sc_generate_screenshot', 'sc_generate_screenshot');

Conclusion/Next Steps

So that’s how I spent 25 minutes and saved myself a ton of time. I’m sure that there will be some growing pains with it, and I intend to take a little more time one day and just hook my Notion to the site – my thinking is that I have websites in Notion and they are all categorized already, and I have subpages that are the screens, so why couldn’t I have one single source of truth? The plan would be to create a button that shoots the data via Make over to my Wordpress site, and at the same time, the API call gets made, and then all is fully automated and I can literally add a site with the press of a button.

The only thing that I may end up dropping for now in is the color scheme. It’s the one thing I haven’t figured out how to reliably automate, and so it may have to be deprecated/pushed back for a bit.

Drop Me A Line