Jump to content

InstagramBasicDisplayApi


nbcommunication

Recommended Posts

Hey @nbcommunication, hey all...

since a few day's (don't know exactly when it startet) I've a problem getting the feed. I'm using this plugin on different sites with various accounts - so far had no problems with it. On 3 of these websites the feed doesn't load any more - on the other websites I suspect that the cache is not expired, yet. There are no items in the $instagram->getMedia() array any more - no errors are shown. When I review the modules settings this so what I got:

There is currently 1 cached request.

2080934359_2023-02-0308_18_16-Modul_InstagramBasicDisplayAPIvieregg-design.deMozillaFirefox.thumb.png.0b8294adf60aa57a14ee06384d1f6eb8.png

Clearing the cache doesn't help.

Anyone else facing this problem, any ideas how I could debug further?

Cheers and many greet!

Link to comment
Share on other sites

Indeed - didn't thought about this place to look for errors 🤦‍♂️

vor 8 Minuten
2023-02-03 09:29:01 	guest 	/ 	Could not process user media: {"error":{"message":"Error validating access token: The session has been invalidated because the user changed their password or Facebook has changed the session for security reasons.","type":"OAuthException","code":190,"fbtrace_id":"A8JC4GfQtIPWPBs_P8YONKd"}}

vor 8 Minuten
2023-02-03 09:29:01 	guest 	/ 	Could not refresh long-lived access token

I expected the access token to be renewed automatically - the passwords of the corresponding pages were not changed.

Link to comment
Share on other sites

Hi @DV-JF,

It happens because Instagram/Facebook invalidates the (authorised) access token if the user changes their password. I guess from the error message (Facebook has changed the session for security reasons) other security issues can lead to it happening too.

It isn't in the scope of the module to notify admins, but it should be pretty easy to implement. Try something like:

<?php

$instagram = $modules->get('InstagramBasicDisplayApi');

$items = $instagram->getMedia();

if(!($items instanceof WireArray) || !$items->count) {
	$cache->getFor($instagram, 'errorNotifyAdmin', 'daily', function() use ($config, $instagram, $log, $mail) {
		return $mail->new()
			->to($config->adminEmail)
			->subject("$instagram error")
			->bodyHTML('<p>' . 
				implode('<br>', $log->getLines(
					$instagram->className(['lowercase' => true]),
					['limit' => 10]
				)) . 
			'</p>')
			->send();
	});
}

This would send the admin an email every day if no media is being returned, with the email being the last 10 log messages.

Cheers,

Chris

  • Thanks 1
Link to comment
Share on other sites

  • 5 months later...

@nbcommunication I think this now has different steps to setup, I will let you know when I get to the bottom of it. Essentially when it comes to adding Instagram testers it is not finding the Instagram username and it is asking for the Facebook username. As they now share logins I am guessing that has something to do with it, I will let you know when I know more, just waiting on the client.

Link to comment
Share on other sites

  • 1 month later...

Hi all,
I am new to processwire, it is my first project with it.
I use this module and it works quite well.
Is it possible to load smaller image versions instead of the orginal from instagram.
I actually only need about 300 x 300px for a gallery that will then be linked to instagram.
At the moment the large originals are loaded which are unnecessarily much data.

I tried to download the images first, which works fine, and then i want to resize them via processwire to show the smaller versions instead of the originals. unfortunately this doesn't work as expected with $filepath->size(300,300) . what am i doing wrong?  

<?php
$instagram = $modules->get('InstagramBasicDisplayApi');
// Get 10 images
$images = $instagram->getImages(10);

$counter = '';
foreach ($images as $image) {
    $counter = $counter + 1;
    $instagramImageUrl = $image->src;
    $imageData = file_get_contents($instagramImageUrl);
    $filename = 'instaBild' . $counter . '.jpg';
    $filePath = $config->paths->assets . "instaTemp/" . $filename;
    file_put_contents($filePath, $imageData);
    $thumb = $filePath->size(300, 300);
    echo $thumb->url;
}
?>

this is certainly not very elegant and above all does not work 😞
i get this error message.
Fatal Error: Uncaught Error: Call to a member function size() on string in site/templates/m_instagram.php:104

how can i process and output the images that are downloaded in the "instaTemp" folder?
or is there a simple way to define the output size in the InstagramBasicDisplayApi module?
i'm sure it is possible but my skills in php and processWire are very limited!

thanks a lot in advance

 

 

 

Link to comment
Share on other sites

Hi @bdbdbd,

The size() method needs to be called on a Pageimage object (an image saves to a Pageimages field) - it can't be called on an image directly. Saving the images to an image field would end up being quite tricky as you'd need to handle checking if the image already exists, and removing old images too.

Honestly, I'd recommend just using the images from instagram, add width="300" height="n" to the <img> tag. I say height=n as this will need to be the height that the image would be if it were 300px wide - they aren't all square images.

<?php
$instagram = $modules->get('InstagramBasicDisplayApi');
// Get 10 images
$images = $instagram->getImages(10);

$counter = '';
foreach ($images as $image) {
    $counter = $counter + 1;
  	$width = 300;
  	$height = round(($image->height / $image->width) * $width);
  	echo "<img src=$image->src alt='$image->alt' width=$width height=$height>";
}
?>

If you need a 300px x 300px image grid, I'd recommend using a <div> with these dimensions and adding the image as a background-image (background-size: cover).

Cheers,

Chris

Link to comment
Share on other sites

hi @nbcommunication,

thanks for your quick response.

6 hours ago, nbcommunication said:

The size() method needs to be called on a Pageimage object (an image saves to a Pageimages field) - it can't be called on an image directly. Saving the images to an image field would end up being quite tricky as you'd need to handle checking if the image already exists, and removing old images too.

yes, that's what i thought, and i was hoping that there would be an easy way to reduce the image size with processwire.

i'm doing it now like this. I reduce the imagesize and do some croping to squared images via php, then save it to the temp-folder and link to this image.

            $instagram = $modules->get('InstagramBasicDisplayApi');
            // Get 10 images
            $images = $instagram->getImages(10);

            $counter = '';
            echo '<div class="start_instagram uk-grid-column-small uk-grid-row-small uk-child-width-1-2@s uk-child-width-1-5@m" uk-grid>';
            foreach ($images as $image) {
                $counter = $counter + 1;
                $instagramImageUrl = $image->src;
                $imageData = file_get_contents($instagramImageUrl);
                $fileName = 'instaBild' . $counter . '.jpg';
                $filePath = $config->paths->assets . "instaTemp/" . $fileName;

                // Verkleinere das Bild auf eine maximale Breite von 300px
                $maxWidth = 300;
                list($width, $height) = getimagesizefromstring($imageData);
                $aspectRatio = $width / $height;
                $newWidth = min($maxWidth, $width);
                $newHeight = $newWidth / $aspectRatio;

                // Erstelle ein leeres quadratisches Bild
                $imageResized = imagecreatetruecolor($maxWidth, $maxWidth);
                $imageSource = imagecreatefromstring($imageData);

                // Berechne die Position für das Cropping (zentriert)
                $cropX = 0;
                $cropY = 0;
                if ($width > $height) {
                    // Querformat, seitlich beschnitten
                    $cropX = ($width - $height) / 2;
                } elseif ($width < $height) {
                    // Hochformat, oben und unten beschnitten
                    $cropY = ($height - $width) / 2;
                }

                // Führe das Cropping durch
                imagecopyresampled($imageResized, $imageSource, 0, 0, $cropX, $cropY, $maxWidth, $maxWidth, $width - (2 * $cropX), $height - (2 * $cropY));

                // Speichere das verkleinerte und beschnittene Bild
                imagejpeg($imageResized, $filePath);

                imagedestroy($imageResized);
                imagedestroy($imageSource);

                // echo $fileName . " auf " . $filePath . " gespeichert.<br>";
                $tempPath = "/site/assets/instaTemp/" . $fileName;
                $altDesc = $image->alt;
                echo "<div>
                <a href='". $image->href ."' target='insta'>
                <img src='" . $tempPath . "' alt='" . $altDesc . "' title='". $altDesc ."'>
                </a>
                </div>";
            }
            echo '</div>';

            ?>

i think this solution is OK, since i have proCache running and so it doesn't regenerate on every page load, or am i wrong?
there are 10 images loaded which are now about 160kb in size as compared to the 3mb for the originals.

suggestions for improvements are still welcome, i wanted to make sure i hadn't overlooked any major option.

  • Thanks 1
Link to comment
Share on other sites

Hi @bdbdbd,

It isn't the way I'd do it but if it is working for you then that's great.

I'd probably add something that checks whether a resized version exists already prior to resizing. If it exists, perhaps update the file modified time if that's possible, and then have a bit of a script to remove images that are older that a set time period, could be anywhere from 1 day to 6 months depending on how often the feed is updated.

Cheers,

Chris

Link to comment
Share on other sites

hi @nbcommunication

24 minutes ago, nbcommunication said:

I'd probably add something that checks whether a resized version exists already prior to resizing. If it exists, perhaps update the file modified time if that's possible, and then have a bit of a script to remove images that are older that a set time period, could be anywhere from 1 day to 6 months depending on how often the feed is updated.

You are absolutely right!
but I don't have the skills and knowledge of processWire and php to do it that way. 🥲

I'm not super happy with my solution, but it reduces the amount of data to be transferred to a good level. it is of course a quick and dirty implementation based on my skills. 🙄

since I don't want to display the images on the page in full resolution but only link to instagram it makes sense not to have to load everything and just squeeze it small in the display.

i read that in the "old" instagram api there was the possibility to request different image versions. this would actually be the perfect solution for the topic. this should potentially interest many users of the InstagramBasicDisplayAPI. Maybe there will be an update from Instagram?

regardless, your module is a huge relief, 1000 thanks for that!

  • Like 1
Link to comment
Share on other sites

  • 3 weeks later...

@nbcommunication For my new project I want to make use of the lazy loading function. So I followed the steps in the documentation.

My main question is: where do I have to put this code?

$instagram = $modules->get('InstagramBasicDisplayApi'); // The pagination cursor is reset if the request is not AJAX, e.g. when the page is loaded.

if($config->ajax) {
    header('Content-Type: application/json');
    echo $instagram->getMedia(); // ['json' => true] is inferred by $config->ajax
    die();
}

echo '<div id=instagram class="uk-grid-small uk-child-width-1-2 uk-child-width-1-3@s uk-child-width-1-4@l" data-uk-grid data-uk-lightbox data-uk-scrollspy="target: > div; cls: uk-animation-slide-bottom-small; delay: 128;"></div>';

It it necessary to enter the user name in the getMedia() function? 

$instagram->getMedia('my.account');

When viewing the page I receive the following error in the console. I guess that has to do with the placement of the code above.

(Index):403 SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON

When placing the code at the top of my template (before the <!DOCTYPE is declared) I receive the following error:

SyntaxError: Unexpected end of JSON input

 

Link to comment
Share on other sites

Hi @Stefanowitsch,

Are you using the javascript from the README? I think the UIkit ajax() function has changed so that could be the issue. You could try:

fetch(window.location.href, {
	headers: {
		'X-Requested-With': 'XMLHttpRequest',
	},
	cache: 'no-store',
})
	.then(response => {
		if (response.ok) {
			return response.json();
		}
		UIkit.util.addClass(instagram.$loading, 'uk-hidden');
		console.error(response.statusText); // ERROR
		return [];
	})
	.then(data => {
		
		// Hide spinner
		UIkit.util.addClass(instagram.$loading, 'uk-hidden');

		if (!UIkit.util.isArray(data) || !data.length) return; // If no items do not render

		const items = [];
		data.forEach(item => {

			switch (item.type) {
				case 'VIDEO':
					items.push(instagram.renderItem(item.poster, item.alt, item.src));
					break;
				case 'CAROUSEL_ALBUM':
					// If 4 or greater items, display a grid of the first 4 images with the rest hidden
					// Otherwise display the main image (no break, moves to default)
					if (item.children.length >= 4) {
						let out = '';
						for (let i = 0; i < item.children.length; i++) {
							out += `<div${i < 4 ? '' : ' class=uk-hidden'}>
								${instagram.renderItem(item.children[i].src, item.alt)}
							</div>`;
						}
						items.push(`<div class="uk-grid-collapse uk-child-width-1-2" data-uk-grid>${out}</div>`);
						break;
					}
				default: // IMAGE
					items.push(instagram.renderItem(item.src, item.alt));
					break;
			}
		});

		const count = items.length;
		if (count) {

			// Wrap all items with a div
			let out = '';
			for (let i = 0; i < count; i++) {
				out += `<div id=instagram-item-${instagram.total + i}>
					${items[i]}
				</div>`;
			}

			// Append items to the container
			UIkit.util.append(instagram.$el, out);

			// Attach scrollspy listener on last item of second last row
			if (count > 5) {
				UIkit.util.on(`#instagram-item-${instagram.total + count - 6}`, 'inview', () => instagram.get());
			}

			// Update total
			instagram.total = instagram.total + count;
		}
	});

The next thing to do would be to try and get the full output that the script is returning. I'm not familiar enough with fetch() to know for certain but you could try 

console.log(response.text());

after the error log to see if that gives you it.

My experience with the errors you've mentioned is that the first is caused by HTML being output/echoed before the JSON, and the second is usually caused by a deprecation notice being output. If you've got $config->debug enabled, try switching that off - if that works then check your logs to see what is triggering the notice and see if it can be resolved.

Hope that helps,

Chris

  • Like 1
Link to comment
Share on other sites

@nbcommunication thanks for your input!

It seems the problem lies somewhere else. I am trying to get the most basic output but all I am receiving is an empty data object.

On my home.php template I have this code at the very top:

<?php

$instagram = $modules->get('InstagramBasicDisplayApi'); // The pagination cursor is reset if the request is not AJAX, e.g. when the page is loaded.

if($config->ajax) {
    header('Content-Type: application/json');
    echo $instagram->getMedia(); // ['json' => true] is inferred by $config->ajax
    bd($instagram->getMedia());
    die();
}
?>

Then inside my template I have this code below which should do nothing else than send an AJAX request to the page itself to execute the code shown above. Also to make tracy debugger log the WireArray that is returned by the $instagram->getMedia() function.

<script>
    (async () => {

        console.log('try to fetch data');
        try {
            const response = await fetch(window.location.href, {
                method: 'GET',
                headers: {
                    'X-Requested-With': 'XMLHttpRequest'
                },
                responseType: 'json'
            });

            if (response.status === 200) {
                const data = await response.text();
                console.log(data);
            }
            else {
                console.log(response.status);
                console.log(response.statusText);
            }

        } catch (error) {
            console.error(error);
        }
    })();
</script>

Wenn viewing the homepage this script is executed but the console.log(data) prints out an empty string.

The bd($instagram->getMedia()) instruction in the header also logs nothing.

Link to comment
Share on other sites

4 hours ago, nbcommunication said:

Hi @Stefanowitsch,

I'd start removing layers of complexity - does getMedia() return something outwith the ajax context?

Is anything being logged by the module in Logs? Does the default user account appear in the module config?

Cheers,

Chris

Yes I have a working instance of the InstagramBasicDisplayApi on my Page. The media is fetched from my account and displayed in a carousel:

image.thumb.png.739abc068b86603aea7b601ad7e872ad.png

This is the code, as I mentioned it works fine:

<ul class="uk-slider-items uk-child-width-1-2 uk-child-width-1-5@m uk-grid">

  <?php

  // Get the 12 most recent items and render them based on type

  foreach($instagram->getMedia('thumann.media',12) as $item) {

    switch($item->type) {
      case 'VIDEO':
        $media_src = $item->poster;
        $media_icon = '<i class="fas fa-film"></i>';
        break;
      case 'CAROUSEL_ALBUM':
        // Just get the first item
        $media_src = $item->children->first->src;
        $media_icon = '<i class="far fa-images"></i>';
        break;
      default:
        $media_src = $item->src;
        $media_icon = '<i class="far fa-images"></i>';
        break;
    }

  ?>

  <li>
    <a href="<?=$item->link?>" target="_blank" rel="noopener">
      <img class="lazyload img-fluid" src="<?=$media_src?>" alt="<?=$item->alt?>"/>
      <?=$media_icon?>
    </a>
  </li>
  <?

    }

  ?>

</ul>

Right now only 12 items are fetched. I want to go the next step and load the content of my Instagram channel in batches and append them when scrolling through the carousel.

So I went to the documentation on the PW modules pages under "Pagination":

https://processwire.com/modules/instagram-basic-display-api/

Lazy loading of items can be achieved using getMedia(). The following example shows how to return batches of items using AJAX requests:
$instagram = $modules->get('InstagramBasicDisplayApi'); // The pagination cursor is reset if the request is not AJAX, e.g. when the page is loaded.

if($config->ajax) {
	header('Content-Type: application/json');
	echo $instagram->getMedia(); // ['json' => true] is inferred by $config->ajax
	die();
}

But when I include a Tracy Debugger bardump on the $instagram->getMedia() function here it returns nothing. 

Link to comment
Share on other sites

5 hours ago, nbcommunication said:

Hi @Stefanowitsch,

Is the module itself logging anything?

You've got your username in the working example - does adding it to the ajax one change anything?

Cheers,

Chris

No it does not change anything.
This logs "false" (when executed in the header of the template in the if $config->ajax statment.

 bd($instagram->getMedia('myusername'))

 

Link to comment
Share on other sites

Hi @Stefanowitsch,

This is odd - getMedia() shouldn't be returning `false`, perhaps the json_encode() of the data is failing, this would explain the false return.

Would you be able to edit the module to add a log before line 439?

<php
    
    $this->log(print_r($data, 1));
    if(count($data)) { // Line 439
        //...
    }

This output might help to determine why it is failing.

Cheers,

Chris

Link to comment
Share on other sites

11 hours ago, nbcommunication said:

Hi @Stefanowitsch,

This is odd - getMedia() shouldn't be returning `false`, perhaps the json_encode() of the data is failing, this would explain the false return.

Would you be able to edit the module to add a log before line 439?

<php
    
    $this->log(print_r($data, 1));
    if(count($data)) { // Line 439
        //...
    }

This output might help to determine why it is failing.

Cheers,

Chris

This outputs an Array containing all the data I would expect. No errors at all.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...