Skip to content

Commit

Permalink
Added support for stories in InstagramBridge. Closes #665
Browse files Browse the repository at this point in the history
Renamed parameters as stories are only available in user mode.
Use a regex instead of HTML parsing to extract the JSON, as it is way faster.
  • Loading branch information
teromene committed May 5, 2018
1 parent c1e3352 commit 8770c87
Showing 1 changed file with 69 additions and 36 deletions.
105 changes: 69 additions & 36 deletions bridges/InstagramBridge.php
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ class InstagramBridge extends BridgeAbstract {
'name' => 'username', 'name' => 'username',
'required' => true 'required' => true
), ),
'media_type' => array( 'media_type_u' => array(
'name' => 'Media type', 'name' => 'Media type',
'type' => 'list', 'type' => 'list',
'required' => false, 'required' => false,
'values' => array( 'values' => array(
'Both' => 'all', 'All' => 'all',
'Story' => 'story',
'Video' => 'video', 'Video' => 'video',
'Picture' => 'picture' 'Picture' => 'picture',
), ),
'defaultValue' => 'all' 'defaultValue' => 'all'
) )
Expand All @@ -29,7 +30,7 @@ class InstagramBridge extends BridgeAbstract {
'name' => 'hashtag', 'name' => 'hashtag',
'required' => true 'required' => true
), ),
'media_type' => array( 'media_type_h' => array(

This comment has been minimized.

Copy link
@mdemoss

mdemoss May 6, 2018

Contributor

Why change the parameter name(s)? This breaks existing users.

This comment has been minimized.

Copy link
@teromene

teromene May 6, 2018

Author Member

The reason for that is that RSS-Bridge supports multiple parameters with the same name if and only if they contain the exact same value.
Here, hashtags don't have stories, so it would not be possible to pass "story" as a parameter.
This is a design mistake that I made when I added support for hashtags.

This comment has been minimized.

Copy link
@mdemoss

mdemoss May 6, 2018

Contributor

I suggest looking for a way to keep compatibility with the old parameters. Is there a way to make a parameter hidden or some other approach that will allow old feed subscriptions with media_type to keep working?

This comment has been minimized.

Copy link
@logmanoriginal

logmanoriginal May 29, 2018

Contributor

I did a little twist on this bridge to make it compatible to the old feed subscriptions. See 064ba45

Throwing error messages is probably not the best solution, but this is what the client error message is for 😄

'name' => 'Media type', 'name' => 'Media type',
'type' => 'list', 'type' => 'list',
'required' => false, 'required' => false,
Expand All @@ -44,27 +45,8 @@ class InstagramBridge extends BridgeAbstract {
); );


public function collectData(){ public function collectData(){
$html = getSimpleHTMLDOM($this->getURI())
or returnServerError('Could not request Instagram.');

$innertext = null;

foreach($html->find('script') as $script) {
if('' === $script->innertext) {
continue;
}

$pos = strpos(trim($script->innertext), 'window._sharedData');
if(0 !== $pos) {
continue;
}

$innertext = $script->innertext;
break;
}


$json = trim(substr($innertext, $pos + 18), ' =;'); $data = $this->getInstagramJSON($this->getURI());
$data = json_decode($json);


if(!is_null($this->getInput('u'))) { if(!is_null($this->getInput('u'))) {
$userMedia = $data->entry_data->ProfilePage[0]->graphql->user->edge_owner_to_timeline_media->edges; $userMedia = $data->entry_data->ProfilePage[0]->graphql->user->edge_owner_to_timeline_media->edges;
Expand All @@ -74,32 +56,83 @@ public function collectData(){


foreach($userMedia as $media) { foreach($userMedia as $media) {
$media = $media->node; $media = $media->node;
// Check media type
switch($this->getInput('media_type')) { if(!is_null($this->getInput('u'))) {
case 'all': break; switch($this->getInput('media_type_u')) {
case 'video': case 'all': break;
if($media->is_video === false) continue 2; case 'video':
break; if($media->__typename != 'GraphVideo') continue 2;
case 'picture': break;
if($media->is_video === true) continue 2; case 'picture':
break; if($media->__typename != 'GraphImage') continue 2;
default: break; break;
case 'story':
if($media->__typename != 'GraphSidecar') continue 2;
break;
default: break;
}
} else {
if($this->getInput('media_type_h') == 'video' && !$media->is_video) continue;
} }


$item = array(); $item = array();
$item['uri'] = self::URI . 'p/' . $media->shortcode . '/'; $item['uri'] = self::URI . 'p/' . $media->shortcode . '/';
$item['content'] = '<img src="' . htmlentities($media->display_url) . '" />';
if (isset($media->edge_media_to_caption->edges[0]->node->text)) { if (isset($media->edge_media_to_caption->edges[0]->node->text)) {
$item['title'] = $media->edge_media_to_caption->edges[0]->node->text; $item['title'] = $media->edge_media_to_caption->edges[0]->node->text;
} else { } else {
$item['title'] = basename($media->display_url); $item['title'] = basename($media->display_url);
} }

if(!is_null($this->getInput('u')) && $media->__typename == 'GraphSidecar') {
$data = $this->getInstagramStory($item['uri']);
$item['content'] = $data[0];
$item['enclosures'] = $data[1];
} else {
$item['content'] = '<img src="' . htmlentities($media->display_url) . '" alt="'. $item["title"] . '" />';
$item['enclosures'] = array($media->display_url);
}

$item['timestamp'] = $media->taken_at_timestamp; $item['timestamp'] = $media->taken_at_timestamp;
$item['enclosures'] = array($media->display_url);
$this->items[] = $item; $this->items[] = $item;
} }
} }


protected function getInstagramStory($uri) {

$data = $this->getInstagramJSON($uri);
$mediaInfo = $data->entry_data->PostPage[0]->graphql->shortcode_media;

//Process the first element, that isn't in the node graph
$caption = $mediaInfo->edge_media_to_caption->edges[0]->node->text;

$enclosures = [$mediaInfo->display_url];
$content = '<img src="' . htmlentities($mediaInfo->display_url) . '" alt="'. $caption . '" />';

foreach($mediaInfo->edge_sidecar_to_children->edges as $media) {

$content .= '<img src="' . htmlentities($media->node->display_url) . '" alt="'. $caption . '" />';
$enclosures[] = $media->node->display_url;

}

return [$content, $enclosures];

}

protected function getInstagramJSON($uri) {

$html = getContents($uri)
or returnServerError('Could not request Instagram.');
$scriptRegex = '/window\._sharedData = (.*);<\/script>/';

preg_match($scriptRegex, $html, $matches, PREG_OFFSET_CAPTURE, 0);

return json_decode($matches[1][0]);

}

public function getName(){ public function getName(){
if(!is_null($this->getInput('u'))) { if(!is_null($this->getInput('u'))) {
return $this->getInput('u') . ' - Instagram Bridge'; return $this->getInput('u') . ' - Instagram Bridge';
Expand Down

0 comments on commit 8770c87

Please sign in to comment.