[ 'text' => [ 'name' => 'Text to search', 'title' => 'Search includes title, description and tags.', 'type' => 'text', 'exampleValue' => 'Text (incl. tags)' ], 'tags' => [ 'name' => 'Tags to search', 'title' => 'Space seperated tags to include in search. Prepend with "-" to exclude, "~" for optional.', 'type' => 'text', 'exampleValue' => 'tag1 -tag2 ~tag3' ], 'order' => [ 'name' => 'Sort by', 'type' => 'list', 'values' => [ 'Trending' => '-hotness_score', 'Newest' => '-date_added', 'Oldest' => 'date_added', 'Top' => '-num_likes', 'Bottom' => 'num_likes' ], 'defaultValue' => '-date_added' ], 'range' => [ 'name' => 'Date range', 'type' => 'list', 'values' => [ 'Today' => 'today', 'Yesterday' => 'yesterday', 'Past 3 days' => '3_days', 'Past week' => 'week', 'Past month' => '30_days', 'Past year' => '365_days', 'All time' => '' ], 'defaultValue' => 'All time' ], 'video_only' => [ 'name' => 'Video only?', 'type' => 'checkbox' ], 'rating_s' => [ 'name' => 'Include SFW', 'type' => 'checkbox' ], 'rating_q' => [ 'name' => 'Include Questionable', 'type' => 'checkbox' ], 'rating_e' => [ 'name' => 'Include NSFW', 'type' => 'checkbox' ] ], 'Post Search' => [ 'tags' => [ 'name' => 'Tags to search', 'title' => 'Space seperated tags to include in search. Prepend with "-" to exclude, "~" for optional.', 'type' => 'text', 'exampleValue' => 'tag1 -tag2 ~tag3' ], 'order' => [ 'name' => 'Sort by', 'type' => 'list', 'values' => [ 'Trending' => '-hotness_score', 'Newest' => '-date_added', 'Oldest' => 'date_added', 'Top' => '-num_likes', 'Bottom' => 'num_likes' ], 'defaultValue' => '-date_added' ], 'range' => [ 'name' => 'Date range', 'type' => 'list', 'values' => [ 'Today' => 'today', 'Yesterday' => 'yesterday', 'Past 3 days' => '3_days', 'Past week' => 'week', 'Past month' => '30_days', 'Past year' => '365_days', 'All time' => '' ], 'defaultValue' => 'All time' ], 'text_only' => [ 'name' => 'Only include posts with text?', 'type' => 'checkbox' ], 'rating_s' => [ 'name' => 'Include SFW', 'type' => 'checkbox' ], 'rating_q' => [ 'name' => 'Include Questionable', 'type' => 'checkbox' ], 'rating_e' => [ 'name' => 'Include NSFW', 'type' => 'checkbox' ] ], 'User profile' => [ 'user' => [ 'name' => 'Username', 'type' => 'text', 'required' => true ], 'user_id' => [ 'name' => 'User ID', 'type' => 'number', 'title' => 'User ID, if known.' ], 'reshares' => [ 'name' => 'Include reshares', 'type' => 'checkbox' ], 'rating_s' => [ 'name' => 'Include SFW', 'type' => 'checkbox' ], 'rating_q' => [ 'name' => 'Include Questionable', 'type' => 'checkbox' ], 'rating_e' => [ 'name' => 'Include NSFW', 'type' => 'checkbox' ] ], 'Home feed' => [ 'order' => [ 'name' => 'Sort by', 'type' => 'list', 'values' => [ 'Trending' => '-hotness_score', 'Newest' => '-date_added' ], 'defaultValue' => '-date_added' ], 'range' => [ 'name' => 'Date range', 'type' => 'list', 'values' => [ 'Today' => 'today', 'Yesterday' => 'yesterday', 'Past 3 days' => '3_days', 'Past week' => 'week', 'Past month' => '30_days', 'Past year' => '365_days', 'All time' => '' ], 'defaultValue' => 'All time' ], 'reshares' => [ 'name' => 'Include reshares', 'type' => 'checkbox' ], 'rating_s' => [ 'name' => 'Include SFW', 'type' => 'checkbox' ], 'rating_q' => [ 'name' => 'Include Questionable', 'type' => 'checkbox' ], 'rating_e' => [ 'name' => 'Include NSFW', 'type' => 'checkbox' ] ] ]; public function collectData() { if ($this->queriedContext === 'Image Search') { $opt = [ 'text' => $this->getInput('text'), 'optional_tags' => [], 'negative_tags' => [], 'required_tags' => [], 'order' => $this->getInput('order'), 'range' => $this->getInput('range'), 'video_only' => $this->getInput('video_only'), 'rating_s' => $this->getInput('rating_s'), 'rating_q' => $this->getInput('rating_q'), 'rating_e' => $this->getInput('rating_e') ]; $tag_arr = explode(' ', $this->getInput('tags') ?? ''); foreach ($tag_arr as $str) { switch ($str[0]) { case '-': $opt['negative_tags'][] = substr($str, 1); break; case '~': $opt['optional_tags'][] = substr($str, 1); break; default: $opt['required_tags'][] = substr($str, 1); break; } } $data = $this->getImagesSearch($opt); foreach ($data['results'] as $record) { $item = $this->getImage($record['id']); $this->addItem($item); } } if ($this->queriedContext === 'Post Search') { $opt = [ 'optional_tags' => [], 'negative_tags' => [], 'required_tags' => [], 'order' => $this->getInput('order'), 'range' => $this->getInput('range'), 'text_only' => $this->getInput('text_only'), 'rating_s' => $this->getInput('rating_s'), 'rating_q' => $this->getInput('rating_q'), 'rating_e' => $this->getInput('rating_e') ]; $tag_arr = explode(' ', $this->getInput('tags')); foreach ($tag_arr as $str) { switch ($str[0]) { case '-': $opt['negative_tags'][] = substr($str, 1); break; case '~': $opt['optional_tags'][] = substr($str, 1); break; default: $opt['required_tags'][] = substr($str, 1); break; } } $data = $this->getPostsSearch($opt); foreach ($data['results'] as $record) { $item = $this->getPost($record['id'], $record); $this->addItem($item); } } if ( $this->queriedContext === 'User profile' || $this->queriedContext === 'Home feed' ) { $opt = [ 'reshares' => $this->getInput('reshares'), 'rating_s' => $this->getInput('rating_s'), 'rating_q' => $this->getInput('rating_q'), 'rating_e' => $this->getInput('rating_e') ]; if ($this->queriedContext === 'User profile') { $opt['order'] = '-date_added'; $opt['range'] = ''; $user_id = $this->getInput('user_id') ?? $this->getOwnerID($this->getInput('user')); $data = $this->getFeedData( $opt, $user_id ); } if ($this->queriedContext === 'Home feed') { $opt['order'] = $this->getInput('order'); $opt['range'] = $this->getInput('range'); $data = $this->getFeedData($opt); } foreach ($data['results'] as $record) { switch ($record['content_type']) { case 'reshare': //get type of reshare and its id $id = $record['content_object']['content_object']['id']; switch ($record['content_object']['content_type']) { case 'galleryimage': $item = $this->getImage($id); $item['title'] = "{$record['owner_username']} shared: {$item['title']}"; break; case 'commission': $item = $this->getCommission($id, $record['content_object']['content_object']); $item['title'] = "{$record['owner_username']} shared: {$item['title']}"; break; case 'post': $item = $this->getPost($id, $record['content_object']['content_object']); $item['title'] = "{$record['owner_username']} shared: {$item['title']}"; break; }; break; case 'galleryimage': $item = $this->getImage($record['content_object']['id']); break; case 'commission': $item = $this->getCommission($record['content_object']['id'], $record['content_object']); break; case 'post': $item = $this->getPost($record['content_object']['id'], $record['content_object']); break; } $this->addItem($item); } } } public function getName() { return self::NAME; } public function getURI() { return self::URI; } private function getImagesSearch(array $opt) { $url = self::URI . "/api/galleries/images/?by_following=false&date_range={$opt['range']}&ordering={$opt['order']}&is_video={$opt['video_only']}"; $url .= "&text={$opt['text']}&visibility=PUBLIC&visibility=PROFILE_ONLY&page=1&page_size=30&format=json"; if (count($opt['optional_tags']) > 0) { foreach ($opt['optional_tags'] as $tag) { $url .= "&optional_tags=$tag"; } } if (count($opt['negative_tags']) > 0) { foreach ($opt['negative_tags'] as $tag) { $url .= "&negative_tags=$tag"; } } if (count($opt['required_tags']) > 0) { foreach ($opt['required_tags'] as $tag) { $url .= "&required_tags=$tag"; } } if ($opt['rating_s']) { $url .= '&maturity_rating=SFW'; } if ($opt['rating_q']) { $url .= '&maturity_rating=Questionable'; } if ($opt['rating_e']) { $url .= '&maturity_rating=NSFW'; } return $this->getData($url, false, true); } private function getPostsSearch(array $opt) { $url = self::URI . "/api/posts/?by_following=false&date_range={$opt['range']}&ordering={$opt['order']}"; $url .= '&visibility=PUBLIC&visibility=PROFILE_ONLY&page=1&page_size=30&format=json'; if (count($opt['optional_tags']) > 0) { foreach ($opt['optional_tags'] as $tag) { $url .= "&optional_tags=$tag"; } } if (count($opt['negative_tags']) > 0) { foreach ($opt['negative_tags'] as $tag) { $url .= "&negative_tags=$tag"; } } if (count($opt['required_tags']) > 0) { foreach ($opt['required_tags'] as $tag) { $url .= "&required_tags=$tag"; } } if ($opt['rating_s']) { $url .= '&maturity_rating=SFW'; } if ($opt['rating_q']) { $url .= '&maturity_rating=Questionable'; } if ($opt['rating_e']) { $url .= '&maturity_rating=NSFW'; } return $this->getData($url, false, true); } private function getFeedData(array $opt, $ownerID = null) { $url = self::URI . "/api/feed/?date_range={$opt['range']}&ordering={$opt['order']}&page=1&page_size=30&format=json"; if (is_null($ownerID)) { $url .= '&visibility=PUBLIC&by_following=false'; } else { $url .= "&owner={$ownerID}"; } if (!$opt['reshares']) { $url .= '&hide_reshares=true'; } if ($opt['rating_s']) { $url .= '&maturity_rating=SFW'; } if ($opt['rating_q']) { $url .= '&maturity_rating=Questionable'; } if ($opt['rating_e']) { $url .= '&maturity_rating=NSFW'; } return $this->getData($url, false, true); } private function getOwnerID($username) { $url = self::URI . "/api/user_profiles/{$username}/?format=json"; $data = $this->getData($url, true, true) or returnServerError("Could not load $url"); return $data['owner']; } private function getPost($id, array $metadata = null) { if (isset($metadata) && count($metadata['gallery_images']) < $metadata['num_images']) { $metadata = null; //force re-fetch of metadata } $uri = self::URI . '/posts/' . $id; $url = self::URI . '/api/posts/' . $id . '/?format=json'; $data = $metadata ?? $this->getData($url, true, true) or returnServerError("Could not load $url"); $content_str = nl2br($data['content']); $content = "

{$content_str}


"; //TODO: Add link and itaku user mention detection and convert into links. if (array_key_exists('tags', $data) && count($data['tags']) > 0) { $tag_types = [ 'ARTIST' => '', 'COPYRIGHT' => '', 'CHARACTER' => '', 'SPECIES' => '', 'GENERAL' => '', 'META' => '' ]; foreach ($data['tags'] as $tag) { $url = self::URI . '/tags/' . $tag['id']; $str = "#{$tag['name']} "; $tag_types[$tag['tag_type']] .= $str; } foreach ($tag_types as $type => $str) { if (strlen($str) > 0) { $content .= "🏷 {$type}: {$str}
"; } } } if (count($data['folders']) > 0) { $content .= '📁 In Folder(s): '; foreach ($data['folders'] as $folder) { $url = self::URI . '/profile/' . $data['owner_username'] . '/posts/' . $folder['id']; $content .= "#{$folder['title']} "; } } $content .= '
'; if (count($data['gallery_images']) > 0) { foreach ($data['gallery_images'] as $media) { $title = $media['title']; $url = self::URI . '/images/' . $media['id']; $src = $media['image_xl']; $content .= '

'; $content .= "{$title}
"; if ($media['is_thumbnail_for_video']) { $url = self::URI . '/api/galleries/images/' . $media['id'] . '/?format=json'; $media_data = $this->getData($url, true, true) or returnServerError("Could not load $url"); $content .= "


'; } } return [ 'uri' => $uri, 'title' => $data['title'], 'timestamp' => $data['date_added'], 'author' => $data['owner_username'], 'content' => $content, 'categories' => ['post'], 'uid' => $uri ]; } private function getCommission($id, array $metadata = null) { $url = self::URI . '/api/commissions/' . $id . '/?format=json'; $uri = self::URI . '/commissions/' . $id; $data = $metadata ?? $this->getData($url, true, true) or returnServerError("Could not load $url"); $content_str = nl2br($data['description']); $content = "

{$content_str}


"; //TODO: Add link and itaku user mention detection and convert into links. if (array_key_exists('tags', $data) && count($data['tags']) > 0) { // $content .= "🏷 Tag(s): "; $tag_types = [ 'ARTIST' => '', 'COPYRIGHT' => '', 'CHARACTER' => '', 'SPECIES' => '', 'GENERAL' => '', 'META' => '' ]; foreach ($data['tags'] as $tag) { $url = self::URI . '/tags/' . $tag['id']; $str = "#{$tag['name']} "; $tag_types[$tag['tag_type']] .= $str; } foreach ($tag_types as $type => $str) { if (strlen($str) > 0) { $content .= "🏷 {$type}: {$str}
"; } } } if (array_key_exists('reference_gallery_sections', $data) && count($data['reference_gallery_sections']) > 0) { $content .= '📁 Example folder(s): '; foreach ($data['folders'] as $folder) { $url = self::URI . '/profile/' . $data['owner_username'] . '/gallery/' . $folder['id']; $folder_name = $folder['title']; if (!is_null($folder['group'])) { $folder_name = $folder['group']['title'] . '/' . $folder_name; } $content .= "#{$folder_name} "; } } $content .= '
'; if (!is_null($data['thumbnail_detail'])) { $content .= '

'; $content .= "{$data['thumbnail_detail']['title']}
"; if ($data['thumbnail_detail']['is_thumbnail_for_video']) { $url = self::URI . '/api/galleries/images/' . $data['thumbnail_detail']['id'] . '/?format=json'; $media_data = $this->getData($url, true, true) or returnServerError("Could not load $url"); $content .= "

'; } return [ 'uri' => $uri, 'title' => "{$data['comm_type']}: {$data['title']}", 'timestamp' => $data['date_added'], 'author' => $data['owner_username'], 'content' => $content, 'categories' => ['commission', $data['comm_type']], 'uid' => $uri ]; } private function getImage($id /* array $metadata = null */) //$metadata disabled due to no essential information available in ./api/feed/ or ./api/galleries/images/ results. { $uri = self::URI . '/images/' . $id; $url = self::URI . '/api/galleries/images/' . $id . '/?format=json'; $data = /* $metadata ?? */ $this->getData($url, true, true) or returnServerError("Could not load $url"); $content_str = nl2br($data['description']); $content = "

{$content_str}


"; //TODO: Add link and itaku user mention detection and convert into links. if (array_key_exists('tags', $data) && count($data['tags']) > 0) { // $content .= "🏷 Tag(s): "; $tag_types = [ 'ARTIST' => '', 'COPYRIGHT' => '', 'CHARACTER' => '', 'SPECIES' => '', 'GENERAL' => '', 'META' => '' ]; foreach ($data['tags'] as $tag) { $url = self::URI . '/tags/' . $tag['id']; $str = "#{$tag['name']} "; $tag_types[$tag['tag_type']] .= $str; } foreach ($tag_types as $type => $str) { if (strlen($str) > 0) { $content .= "🏷 {$type}: {$str}
"; } } } if (array_key_exists('sections', $data) && count($data['sections']) > 0) { $content .= '📁 In Folder(s): '; foreach ($data['sections'] as $folder) { $url = self::URI . '/profile/' . $data['owner_username'] . '/gallery/' . $folder['id']; $folder_name = $folder['title']; if (!is_null($folder['group'])) { $folder_name = $folder['group']['title'] . '/' . $folder_name; } $content .= "#{$folder_name} "; } } $content .= '
'; if (array_key_exists('is_thumbnail_for_video', $data)) { $url = self::URI . '/api/galleries/images/' . $data['id'] . '/?format=json'; $media_data = $this->getData($url, true, true) or returnServerError("Could not load $url"); $content .= "