array( 'u' => array( 'name' => 'username', 'required' => true ) ), 'Hashtag' => array( 'h' => array( 'name' => 'hashtag', 'required' => true ) ), 'Location' => array( 'l' => array( 'name' => 'location', 'required' => true ) ), 'global' => array( 'media_type' => array( 'name' => 'Media type', 'type' => 'list', 'required' => false, 'values' => array( 'All' => 'all', 'Story' => 'story', 'Video' => 'video', 'Picture' => 'picture', ), 'defaultValue' => 'all' ), 'direct_links' => array( 'name' => 'Use direct image links', 'type' => 'checkbox', ) ) ); const USER_QUERY_HASH = '58b6785bea111c67129decbe6a448951'; const TAG_QUERY_HASH = '174a5243287c5f3a7de741089750ab3b'; const STORY_QUERY_HASH = '865589822932d1b43dfe312121dd353a'; protected function getInstagramUserId($username) { if(is_numeric($username)) return $username; $cacheFac = new CacheFactory(); $cacheFac->setWorkingDir(PATH_LIB_CACHES); $cache = $cacheFac->create(Configuration::getConfig('cache', 'type')); $cache->setScope(get_called_class()); $cache->setKey(array($username)); $key = $cache->loadData(); if($key == null) { $data = getContents(self::URI . 'web/search/topsearch/?query=' . $username); foreach(json_decode($data)->users as $user) { if($user->user->username === $username) { $key = $user->user->pk; } } if($key == null) { returnServerError('Unable to find username in search result.'); } $cache->saveData($key); } return $key; } 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()); if(!is_null($this->getInput('u'))) { $userMedia = $data->data->user->edge_owner_to_timeline_media->edges; } elseif(!is_null($this->getInput('h'))) { $userMedia = $data->data->hashtag->edge_hashtag_to_media->edges; } elseif(!is_null($this->getInput('l'))) { $userMedia = $data->entry_data->LocationsPage[0]->graphql->location->edge_location_to_media->edges; } 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; } $item = array(); $item['uri'] = self::URI . 'p/' . $media->shortcode . '/'; if (isset($media->owner->username)) { $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)'; } $item['title'] = ($media->is_video ? '▶ ' : '') . trim($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); } $item['timestamp'] = $media->taken_at_timestamp; $this->items[] = $item; } } protected function getInstagramStory($uri) { $shortcode = explode('/', $uri)[4]; $data = getContents(self::URI . 'graphql/query/?query_hash=' . self::STORY_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); } protected function getInstagramJSON($uri) { if(!is_null($this->getInput('u'))) { $userId = $this->getInstagramUserId($this->getInput('u')); $data = getContents(self::URI . 'graphql/query/?query_hash=' . self::USER_QUERY_HASH . '&variables={"id"%3A"' . $userId . '"%2C"first"%3A10}'); return json_decode($data); } elseif(!is_null($this->getInput('h'))) { $data = getContents(self::URI . 'graphql/query/?query_hash=' . self::TAG_QUERY_HASH . '&variables={"tag_name"%3A"' . $this->getInput('h') . '"%2C"first"%3A10}'); return json_decode($data); } else { $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(){ if(!is_null($this->getInput('u'))) { return $this->getInput('u') . ' - Instagram Bridge'; } return parent::getName(); } public function getURI(){ if(!is_null($this->getInput('u'))) { return self::URI . urlencode($this->getInput('u')) . '/'; } elseif(!is_null($this->getInput('h'))) { return self::URI . 'explore/tags/' . urlencode($this->getInput('h')); } elseif(!is_null($this->getInput('l'))) { return self::URI . 'explore/locations/' . urlencode($this->getInput('l')); } return parent::getURI(); } }