0

I have this class to scrape instagram pictures and save them locally, the function get_local_photo_links() is where the issue is:

<?php

class Scrape_Instagram {

    private $images = array();

    public function __construct( $username ) {
        $this->username = $username;
    }

    private function build_endpoint() {
        /**
        * Builds the endpoint for the Instagram API, provided the username from constructor.
        * @param $end_cursor    Used in case the user wants more pictures to be scrapped.
        */
        return 'https://www.instagram.com/' . trim( strtolower( $this->username ) ) . '/?__a=1';
    }

    private function get_json() {
        /**
        * GET's the data from Instagram, provided the endpoint.
        */
        $request = wp_remote_get( $this->build_endpoint() );

        if( is_wp_error( $request ) ) {
            return new WP_Error( 'site-down', esc_html__( 'Instagram may be down. Unable to communicate.', '_s') );
        }

        elseif( wp_remote_retrieve_response_code( $request ) !== 200 ) {
            return new WP_Error( 'invalid-response', esc_html__( 'Got an invalid response.', '_s') );
        }

        else {
            return wp_remote_retrieve_body( $request );
        }
    }

    private function get_data() {
        /**
        * Ingests the data from get_json() then converts / decodes into an array.
        */
        $json = $this->get_json();

        if( is_wp_error( $json ) ) {
            return new WP_Error( 'invalid-json', esc_html__( 'Something is wrong. Did you enter the right username?', '_s' ) );
        }

        $data = json_decode( $json, true);

        if( is_wp_error( $data ) ) {
            return new WP_Error( 'invalid-data', esc_html__( 'Something is wrong. Did you enter the right username?', '_s' ) );
        } else {
            return $data;
        }
    }


    private function get_links() {
        /**
        * Based on the array generated from get_data(), some nodes have resulted that contain information about
        * each photo the user has, as such, we'll loop through each photo and access any data.
        * @see ['user']['media']['nodes'] - individual node / image.
        */
        $response = $this->get_data();

        if( is_wp_error( $response ) ) {
            return new WP_Error( 'invalid-json', esc_html__( 'Ouch. The data was not parsed correctly. Cannot continue.' ) );
        } else {
            foreach( $response['user']['media']['nodes'] as $node ) {
                $image = array('real_link' => 'src' );
                $image['real_link'] = 'https://www.instagram.com/p/' . $node['code'] . '?taken-by=' . $this->username;
                $image['src'] = $node['thumbnail_resources'][0]['src'];
                $images_links[] = $image;
            }
        }
        return $images_links;
    }

    public function get_instagram_photos_links() {
        $links = $this->get_links();

        if( is_wp_error( $links) ) {
            return new WP_error( 'unknown-error', esc_html__( 'Something went wrong. Cannot get data. Check username or contact developer.') );
        } else {
            return $links;
        }
    }

    public function get_local_photo_links() {
        /**
        * Requires loading of the wp-admin scripts.
        */
        require_once ('wp-load.php');
        require_once ('wp-admin/includes/admin.php');

        $urls = $this->get_instagram_photos_links();

        $local_images   = array();

        foreach( $urls as $url ) {

            $tmp = download_url( $url['src'] );

            $file_array = array(
                'name'      => basename( $url['src'] ),
                'tmp_name'   => $tmp
            );

            $wp_upload_dir = wp_get_upload_dir();
            $local_url = $wp_upload_dir['path'] . '/' . $file_array['name'];

            if( file_exists( $local_url ) ) {
                echo "Image already exists!";
                array_push( $local_images, ($wp_upload_dir['url'] . '/' . $file_array['name']) );
            } else {
                echo "Image doesn't exist!";
                if ( is_wp_error( $tmp ) ) {
                    @unlink( $file_array[ 'tmp_name' ] );
                    return $tmp;
                }

                $post_id = '0';

                $id = media_handle_sideload( $file_array, $post_id );

                if ( is_wp_error( $id ) ) {
                    @unlink( $file_array['tmp_name'] );
                    return $id;
                }

                $value = wp_get_attachment_url( $id );

                array_push( $local_images, $value );
            }
        }

        return $local_images;
    }
}

Namely with the lines:

$post_id = '0';

$id = media_handle_sideload( $file_array, $post_id );

The $post_id is problematic, because if it's set to 0, for some reason, it defaults to /uploads/2017/12 instead of the current /uploads/2018/01 (note: as of the writing of the question, it's January, 15th, 2018). So it goes back a month.

That means it's trying to attach the images to a post with the id of 0, that was posted in December, 2017.

Looking at my posts, there could be 4 culprits:

enter image description here

Then there's the database with wp_posts itself:

enter image description here

Clearly, there's no post with ID 0 but when we set the post_id to -1, as in $post_id = '-1';, there's no problem, it suddenly works, meaning that somewhere, there has to be a post with the ID of 0.

Running the query:

SELECT wp_posts.* FROM wp_posts 
WHERE 1=1 
AND wp_posts.ID = '0' 

Returns nothing, as follows:

enter image description here

Interestingly enough, when this happens, a post has its thumbnail removed:

enter image description here

The ID of this post is 71, there's no relation.

And, at times, it works without a problem.

Please note that the media upload structure still uses the year/month format, it works if I change the format to just /uploads/ without the y/m format, but I'd like for my script to work on defaults.

What can I do?

9
  • In which context are you calling this function? get_post( 0 ) usually means "get the current post" and at first glance it seems that this is what's happening inside media_handle_sideload(). To debug this, check the output of get_post( 0 ) in your function. – swissspidy Jan 17 '18 at 16:37
  • get_post only returns the current post if you pass nothing as ID. I suggest looking at media_handle_sideload in source and tracing exactly what it does, and checking these things yourself. Specifically, it gets the current timestamp with $time = current_time( 'mysql' ), and later passes that to wp_upload_dir( $time ) to get the correct upload directory. – Milo Jan 17 '18 at 16:42
  • @swissspidy Incredible. It seems the context is a random post, here's a raw dump of var_dump( $post_id ); $post_id='0'; : pastebin.com/NmZnDxRu So even if I'm on the homepage, with a post id of 0, it still takes that post's context. – Jonathan Guerin Jan 17 '18 at 16:54
  • 1
    @Milo Not 100% true. It uses empty() to check whether you passed an ID. empty( 0 ) returns true in PHP. @JonathanGuerin Does your home page show the X latest posts? If so, is the post with ID 6 in that list? The selection isn't really random as get_post() simply uses $GLOBALS['post']. You could temporarily set that var to null before calling media_handle_sideload(). Also, this behavior in media_handle_sideload() might qualify as a bug in WordPress itself. – swissspidy Jan 17 '18 at 17:02
  • @swissspidy Yup. Homepage shows X latest posts. It seems it always picks the latest post. Also, yup, setting the global fixes it. – Jonathan Guerin Jan 17 '18 at 17:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.