From 48b016467641b64f5fc3bbcba61464d4a4db081c Mon Sep 17 00:00:00 2001 From: Shuto Yano Date: Sun, 1 Dec 2019 19:25:20 +0900 Subject: [PATCH] [InstagramBridge] Fix instagram GraphSidecar output and Video embedding (#1361) * [InstagramBridge] Fix GraphSidecar output Fix following issues which related to output of the GraphSidecar type posts. - The GraphSidecar post's media wasn't outputted except for first picture when searching by hashtag or location - Video didn't embedded NOTE: The function getInstagramStory() which was called when the post type is GraphSidecar didn't seem to work just as one intended. Because the web request called in that function is just to get the media of single post, NOT to get the media of Story. But I don't have any idea to solve #694, so it seems be better to rename these function and member variable properly. --- bridges/InstagramBridge.php | 165 ++++++++++++++++++++---------------- 1 file changed, 94 insertions(+), 71 deletions(-) diff --git a/bridges/InstagramBridge.php b/bridges/InstagramBridge.php index fe712b86..679c4c0e 100644 --- a/bridges/InstagramBridge.php +++ b/bridges/InstagramBridge.php @@ -32,14 +32,14 @@ class InstagramBridge extends BridgeAbstract { 'required' => false, 'values' => array( 'All' => 'all', - 'Story' => 'story', 'Video' => 'video', 'Picture' => 'picture', + 'Multiple' => 'multiple', ), 'defaultValue' => 'all' ), 'direct_links' => array( - 'name' => 'Use direct image links', + 'name' => 'Use direct media links', 'type' => 'checkbox', ) ) @@ -48,7 +48,7 @@ class InstagramBridge extends BridgeAbstract { const USER_QUERY_HASH = '58b6785bea111c67129decbe6a448951'; const TAG_QUERY_HASH = '174a5243287c5f3a7de741089750ab3b'; - const STORY_QUERY_HASH = '865589822932d1b43dfe312121dd353a'; + const SHORTCODE_QUERY_HASH = '865589822932d1b43dfe312121dd353a'; protected function getInstagramUserId($username) { @@ -79,11 +79,6 @@ class InstagramBridge extends BridgeAbstract { } public function collectData(){ - - if(is_null($this->getInput('u')) && $this->getInput('media_type') == 'story') { - returnClientError('Stories are not supported for hashtags nor locations!'); - } - $directLink = !is_null($this->getInput('direct_links')) && $this->getInput('direct_links'); $data = $this->getInstagramJSON($this->getURI()); @@ -99,22 +94,18 @@ class InstagramBridge extends BridgeAbstract { foreach($userMedia as $media) { $media = $media->node; - if(!is_null($this->getInput('u'))) { - switch($this->getInput('media_type')) { - case 'all': break; - case 'video': - if($media->__typename != 'GraphVideo') continue 2; - break; - case 'picture': - if($media->__typename != 'GraphImage') continue 2; - break; - case 'story': - if($media->__typename != 'GraphSidecar') continue 2; - break; - default: break; - } - } else { - if($this->getInput('media_type') == 'video' && !$media->is_video) continue; + switch($this->getInput('media_type')) { + case 'all': break; + case 'video': + if($media->__typename != 'GraphVideo' || !$media->is_video) continue 2; + break; + case 'picture': + if($media->__typename != 'GraphImage') continue 2; + break; + case 'multiple': + if($media->__typename != 'GraphSidecar') continue 2; + break; + default: break; } $item = array(); @@ -124,73 +115,105 @@ class InstagramBridge extends BridgeAbstract { $item['author'] = $media->owner->username; } - if (isset($media->edge_media_to_caption->edges[0]->node->text)) { - $textContent = $media->edge_media_to_caption->edges[0]->node->text; - } else { - $textContent = '(no text)'; - } + $textContent = $this->getTextContent($media); - $item['title'] = ($media->is_video ? '▶ ' : '') . trim($textContent); + $item['title'] = ($media->is_video ? '▶ ' : '') . $textContent; $titleLinePos = strpos(wordwrap($item['title'], 120), "\n"); if ($titleLinePos != false) { $item['title'] = substr($item['title'], 0, $titleLinePos) . '...'; } - if(!is_null($this->getInput('u')) && $media->__typename == 'GraphSidecar') { - - $data = $this->getInstagramStory($item['uri']); - $item['content'] = $data[0]; - $item['enclosures'] = $data[1]; - } else { - if($directLink) { - $mediaURI = $media->display_url; - } else { - $mediaURI = self::URI . 'p/' . $media->shortcode . '/media?size=l'; - } - $item['content'] = ''; - $item['content'] .= '' . $item['title'] . ''; - $item['content'] .= '

' . nl2br(htmlentities($textContent)); - $item['enclosures'] = array($mediaURI); + switch($media->__typename) { + case 'GraphSidecar': + $data = $this->getInstagramSidecarData($item['uri'], $item['title']); + $item['content'] = $data[0]; + $item['enclosures'] = $data[1]; + break; + case 'GraphImage': + if($directLink) { + $mediaURI = $media->display_url; + } else { + $mediaURI = self::URI . 'p/' . $media->shortcode . '/media?size=l'; + } + $item['content'] = ''; + $item['content'] .= '' . $item['title'] . ''; + $item['content'] .= '

' . nl2br(htmlentities($textContent)); + $item['enclosures'] = array($mediaURI); + break; + case 'GraphVideo': + $data = $this->getInstagramVideoData($item['uri']); + $item['content'] = $data[0]; + if($directLink) { + $item['enclosures'] = $data[1]; + } else { + $item['enclosures'] = array(self::URI . 'p/' . $media->shortcode . '/media?size=l'); + } + break; + default: break; } - $item['timestamp'] = $media->taken_at_timestamp; $this->items[] = $item; } } - protected function getInstagramStory($uri) { + // returns Sidecar(a post which has multiple media)'s contents and enclosures + protected function getInstagramSidecarData($uri, $postTitle) { + $mediaInfo = $this->getSinglePostData($uri); + $textContent = $this->getTextContent($mediaInfo); + + $enclosures = array(); + $content = ''; + foreach($mediaInfo->edge_sidecar_to_children->edges as $singleMedia) { + $singleMedia = $singleMedia->node; + if($singleMedia->is_video) { + if(in_array($singleMedia->video_url, $enclosures)) continue; // check if not added yet + $content .= '
'; + array_push($enclosures, $singleMedia->video_url); + } else { + if(in_array($singleMedia->display_url, $enclosures)) continue; // check if not added yet + $content .= ''; + $content .= '' . $postTitle . ''; + $content .= '
'; + array_push($enclosures, $singleMedia->display_url); + } + } + $content .= '
' . nl2br(htmlentities($textContent)); + + return array($content, $enclosures); + } + + // returns Video post's contents and enclosures + protected function getInstagramVideoData($uri) { + $mediaInfo = $this->getSinglePostData($uri); + + $textContent = $this->getTextContent($mediaInfo); + $content = '
'; + $content .= '
' . nl2br(htmlentities($textContent)); + + return array($content, array($mediaInfo->video_url)); + } + + protected function getTextContent($media) { + $textContent = '(no text)'; + //Process the first element, that isn't in the node graph + if (count($media->edge_media_to_caption->edges) > 0) { + $textContent = trim($media->edge_media_to_caption->edges[0]->node->text); + } + return $textContent; + } + + protected function getSinglePostData($uri) { $shortcode = explode('/', $uri)[4]; $data = getContents(self::URI . 'graphql/query/?query_hash=' . - self::STORY_QUERY_HASH . - '&variables={"shortcode"%3A"' . + self::SHORTCODE_QUERY_HASH . + '&variables={"shortcode"%3A"' . $shortcode . '"}'); - $mediaInfo = json_decode($data)->data->shortcode_media; - - //Process the first element, that isn't in the node graph - if (count($mediaInfo->edge_media_to_caption->edges) > 0) { - $caption = $mediaInfo->edge_media_to_caption->edges[0]->node->text; - } else { - $caption = ''; - } - - $enclosures = array($mediaInfo->display_url); - $content = '' . $caption . ''; - - foreach($mediaInfo->edge_sidecar_to_children->edges as $media) { - $display_url = $media->node->display_url; - if(!in_array($display_url, $enclosures)) { // add only if not added yet - $content .= '' . $caption . ''; - $enclosures[] = $display_url; - } - } - - return array($content, $enclosures); - + return json_decode($data)->data->shortcode_media; } protected function getInstagramJSON($uri) {