fivehpx.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. <?php
  2. class fivehpx{
  3. public function __construct(){
  4. include "lib/backend.php";
  5. $this->backend = new backend("fivehpx");
  6. include "lib/fuckhtml.php";
  7. $this->fuckhtml = new fuckhtml();
  8. }
  9. public function getfilters($page){
  10. return [
  11. "sort" => [
  12. "display" => "Sort",
  13. "option" => [
  14. "relevance" => "Relevance",
  15. "pulse" => "Pulse",
  16. "newest" => "Newest",
  17. "aqs" => "AI Score"
  18. ]
  19. ],
  20. "category" => [
  21. "display" => "Category",
  22. "option" => [
  23. "any" => "Any category",
  24. "landscape,nature" => "Landscape",
  25. "city" => "City",
  26. "events,sports" => "Events",
  27. "creative" => "Creative"
  28. ]
  29. ],/*
  30. "gear" => [
  31. "display" => "Gear",
  32. "option" => [
  33. "any" => "Any gear",
  34. "camera" => "Camera",
  35. "phone" => "Phone"
  36. ]
  37. ],*/
  38. "downloadable" => [
  39. "display" => "Downloadable",
  40. "option" => [
  41. "any" => "Doesn't matter",
  42. "yes" => "Yes"
  43. ]
  44. ]
  45. ];
  46. }
  47. private function get($proxy, $url, $get = [], $post_data = null){
  48. $curlproc = curl_init();
  49. if($get !== []){
  50. $get = http_build_query($get);
  51. $url .= "?" . $get;
  52. }
  53. curl_setopt($curlproc, CURLOPT_URL, $url);
  54. curl_setopt($curlproc, CURLOPT_ENCODING, ""); // default encoding
  55. if($post_data === null){
  56. curl_setopt($curlproc, CURLOPT_HTTPHEADER,
  57. ["User-Agent: " . config::USER_AGENT,
  58. "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
  59. "Accept-Language: en-US,en;q=0.5",
  60. "Accept-Encoding: gzip, deflate, br, zstd",
  61. "DNT: 1",
  62. "Sec-GPC: 1",
  63. "Connection: keep-alive",
  64. "Upgrade-Insecure-Requests: 1",
  65. "Sec-Fetch-Dest: document",
  66. "Sec-Fetch-Mode: navigate",
  67. "Sec-Fetch-Site: same-origin",
  68. "Sec-Fetch-User: ?1",
  69. "Priority: u=0, i",
  70. "TE: trailers"]
  71. );
  72. }else{
  73. curl_setopt($curlproc, CURLOPT_HTTPHEADER,
  74. ["User-Agent: " . config::USER_AGENT,
  75. "Accept: application/graphql-response+json,application/json;q=0.9",
  76. "Accept-Language: en-CA,en-US;q=0.9,en;q=0.8",
  77. "Accept-Encoding: gzip, deflate, br, zstd",
  78. "Referer: https://500px.com/",
  79. "content-type: application/json",
  80. "x-500px-platform: Web",
  81. "Content-Length: " . strlen($post_data),
  82. "Origin: https://500px.com",
  83. "DNT: 1",
  84. "Sec-GPC: 1",
  85. "Alt-Used: api-neo.500px.com",
  86. "Connection: keep-alive",
  87. "Cookie: x-500px-device-id-prod=a7b57e48-e477-4382-8c43-70c74415eb96",
  88. "Sec-Fetch-Dest: empty",
  89. "Sec-Fetch-Mode: cors",
  90. "Sec-Fetch-Site: same-site",
  91. "Priority: u=4",
  92. "TE: trailers"]
  93. );
  94. // set post data
  95. curl_setopt($curlproc, CURLOPT_POST, true);
  96. curl_setopt($curlproc, CURLOPT_POSTFIELDS, $post_data);
  97. }
  98. curl_setopt($curlproc, CURLOPT_RETURNTRANSFER, true);
  99. curl_setopt($curlproc, CURLOPT_SSL_VERIFYHOST, 2);
  100. curl_setopt($curlproc, CURLOPT_SSL_VERIFYPEER, true);
  101. curl_setopt($curlproc, CURLOPT_CONNECTTIMEOUT, 30);
  102. curl_setopt($curlproc, CURLOPT_TIMEOUT, 30);
  103. // http2 bypass
  104. curl_setopt($curlproc, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
  105. $this->backend->assign_proxy($curlproc, $proxy);
  106. $data = curl_exec($curlproc);
  107. if(curl_errno($curlproc)){
  108. throw new Exception(curl_error($curlproc));
  109. }
  110. curl_close($curlproc);
  111. return $data;
  112. }
  113. public function image($get){
  114. if($get["npt"]){
  115. [$variables, $proxy] =
  116. $this->backend->get(
  117. $get["npt"], "images"
  118. );
  119. $variables = json_decode($variables, true);
  120. }else{
  121. $search = $get["s"];
  122. if(strlen($search) === 0){
  123. throw new Exception("Search term is empty!");
  124. }
  125. $proxy = $this->backend->get_ip();
  126. $variables = [
  127. "type" => "TEXT",
  128. "sort" => strtoupper($get["sort"]),
  129. "first" => 20,
  130. "keyword" => $search,
  131. "resourceTypes" => ["PHOTO"]
  132. ];
  133. if($get["downloadable"] != "any"){
  134. $variables["downloadable"] = "ALLOW";
  135. }
  136. if($get["category"] != "any"){
  137. $variables["categories"] = [strtoupper($get["category"])];
  138. }
  139. /* for some reason this filter doesnt do anything? lol
  140. if($get["gear"] != "any"){
  141. $variables["equipments"] = [strtoupper($get["gear"])];
  142. }*/
  143. }
  144. try{
  145. $json =
  146. $this->get(
  147. $proxy,
  148. "https://api-neo.500px.com/graphql",
  149. [],
  150. json_encode([
  151. "operationName" => "searchResource",
  152. "variables" => $variables,
  153. "extensions" => [
  154. "clientLibrary" => [
  155. "name" => "@apollo/client",
  156. "version" => "4.1.6"
  157. ]
  158. ],
  159. "query" => "query searchResource(\$keyword: String!, \$type: SearchType = TEXT, \$first: Int!, \$after: String, \$resourceTypes: [SearchResourceType!], \$categories: [String!], \$equipments: [String!], \$downloadable: PhotoDownloadable, \$sort: SearchSortOption = RELEVANCE, \$excludeNsfw: Boolean) {\n searchResource(\n keyword: \$keyword\n type: \$type\n first: \$first\n after: \$after\n resourceTypes: \$resourceTypes\n categories: \$categories\n equipments: \$equipments\n downloadable: \$downloadable\n sort: \$sort\n excludeNsfw: \$excludeNsfw\n ) {\n edges {\n cursor\n node {\n __typename\n ... on Photo {\n id\n isDeleted\n isInReview\n isPrivate\n questInfo {\n rewardQuestName\n __typename\n }\n honors {\n ...PhotoHonorFields\n __typename\n }\n title\n description\n downloadable\n licensing {\n status\n __typename\n }\n urls {\n size_600\n size_1024\n __typename\n }\n uploader {\n id\n username\n avatar\n displayName\n isBlockedByMe\n isFollowedByMe\n isFollowingMe\n membership {\n membership\n __typename\n }\n __typename\n }\n isNsfw\n isLikedByMe\n viewerHasReposted\n width\n height\n dominantColorLight\n dominantColorDark\n hasComment\n uploadedAt\n __typename\n }\n ... on Gallery {\n id\n isDeleted\n isPrivate\n kind\n honors {\n ...PhotoHonorFields\n __typename\n }\n name\n itemCount\n hasComment\n isLikedByMe\n isNsfw\n viewerHasReposted\n creator {\n id\n username\n displayName\n avatar\n isFollowedByMe\n isFollowingMe\n isBlockedByMe\n membership {\n membership\n __typename\n }\n __typename\n }\n coverPhotos(first: 5) {\n id\n urls {\n size_600\n size_1024\n __typename\n }\n __typename\n }\n __typename\n }\n ... on PhotoGroup {\n id\n isDeleted\n isInReview\n isPrivate\n honors {\n ...PhotoHonorFields\n __typename\n }\n title\n description\n numCount\n publicItemCount\n isLikedByMe\n isNsfw\n viewerHasReposted\n uploader {\n id\n username\n displayName\n avatar\n isFollowedByMe\n isFollowingMe\n isBlockedByMe\n membership {\n membership\n __typename\n }\n __typename\n }\n coverPhotos(first: 5) {\n id\n urls {\n size_600\n size_1024\n __typename\n }\n __typename\n }\n __typename\n }\n ... on User {\n id\n username\n displayName\n avatar\n location\n isBlockedByMe\n isFollowedByMe\n isFollowingMe\n membership {\n membership\n __typename\n }\n userCoverPhotos: coverPhotos(first: 6) {\n id\n urls {\n size_1024\n __typename\n }\n __typename\n }\n __typename\n }\n ... on Video {\n id\n isDeleted\n isInReview\n isPrivate\n questInfo {\n rewardQuestName\n __typename\n }\n honors {\n ...PhotoHonorFields\n __typename\n }\n title\n coverUrl\n videoUrl\n width\n height\n isNsfw\n isLikedByMe\n uploader {\n id\n username\n avatar\n displayName\n isBlockedByMe\n isFollowedByMe\n isFollowingMe\n membership {\n membership\n __typename\n }\n __typename\n }\n __typename\n }\n }\n __typename\n }\n pageInfo {\n hasNextPage\n endCursor\n __typename\n }\n totalCount\n __typename\n }\n}\n\nfragment PhotoHonorFields on PhotoHonor {\n __typename\n ... on PhotoHonorSelected {\n type\n __typename\n }\n ... on PhotoHonorAmbassadorsPick {\n ambassador {\n id\n avatar\n __typename\n }\n __typename\n }\n ... on PhotoHonorPxGallery {\n gallery {\n id\n name\n __typename\n }\n __typename\n }\n}"
  160. ])
  161. );
  162. }catch(Exception $error){
  163. throw new Exception("Failed to fetch graphQL object");
  164. }
  165. $json = json_decode($json, true);
  166. if($json === null){
  167. throw new Exception("Failed to decode graphQL object");
  168. }
  169. if(isset($json["errors"][0]["message"])){
  170. throw new Exception("500px returned an API error: " . $json["errors"][0]["message"]);
  171. }
  172. if(!isset($json["data"]["searchResource"]["edges"])){
  173. throw new Exception("No edges returned by API");
  174. }
  175. $out = [
  176. "status" => "ok",
  177. "npt" => null,
  178. "image" => []
  179. ];
  180. foreach($json["data"]["searchResource"]["edges"] as $image){
  181. $image = $image["node"];
  182. $title =
  183. trim(
  184. $this->fuckhtml
  185. ->getTextContent(
  186. $image["title"]
  187. ) . ": " .
  188. $this->fuckhtml
  189. ->getTextContent(
  190. $image["description"]
  191. )
  192. , " :"
  193. );
  194. $small = $this->image_ratio(600, $image["width"], $image["height"]);
  195. //$large = $this->image_ratio(2048, $image["width"], $image["height"]);
  196. $large = $this->image_ratio(3840, $image["width"], $image["height"]);
  197. // for some fuck ass reason the invalid signature error gives me the
  198. // signature key they use, lol
  199. // signStrPreview:
  200. // 67gfdsgf!5uiyoi3fdsphoto/37070890/0.jpg21790132400
  201. // 67gfdsgf!5uiyoi3fdsphoto/211496303/0.jpg111790132400
  202. // hash("sha256", <-key->photo/<-url id->/0.jpg<-size-><-expiry->)
  203. // https://cdn-resize-prod-com.500px.cloud/photo/211496303/0.jpg?s=1&expiry=1790132400&sig=b2873066126c294d7ca0b6f74db7f2626d79d5337a4c71add910f699734fade2
  204. $images = [];
  205. preg_match(
  206. '/photo\/([0-9]+)/',
  207. $image["urls"]["size_600"],
  208. $image_id
  209. );
  210. if(count($image_id) !== 0){
  211. $expiry = time() + 3600;
  212. $images[] = [
  213. "url" =>
  214. "https://cdn-resize-prod-com.500px.cloud/photo/{$image_id[1]}/0.jpg?s=4&expiry={$expiry}&sig=" .
  215. hash("sha256", "67gfdsgf!5uiyoi3fdsphoto/{$image_id[1]}/0.jpg4{$expiry}"),
  216. "width" => $large[0] > $image["width"] ? $image["width"] : $large[0],
  217. "height" => $large[1] > $image["height"] ? $image["height"] : $large[1]
  218. ];
  219. }
  220. // add thumbnail
  221. $images[] = [
  222. "url" => $image["urls"]["size_600"],
  223. "width" => $small[0],
  224. "height" => $small[1]
  225. ];
  226. $out["image"][] = [
  227. "title" => $title,
  228. "source" => $images,
  229. "url" => "https://500px.com/photo/{$image["id"]}"
  230. ];
  231. }
  232. // get NPT token
  233. if($json["data"]["searchResource"]["pageInfo"]["hasNextPage"] === true){
  234. $variables["after"] = $json["data"]["searchResource"]["pageInfo"]["endCursor"];
  235. $out["npt"] =
  236. $this->backend->store(
  237. json_encode($variables),
  238. "images",
  239. $proxy
  240. );
  241. }
  242. return $out;
  243. }
  244. private function image_ratio($longest_edge, $width, $height){
  245. $ratio = [
  246. $longest_edge / $width,
  247. $longest_edge / $height
  248. ];
  249. if($ratio[0] < $ratio[1]){
  250. $ratio = $ratio[0];
  251. }else{
  252. $ratio = $ratio[1];
  253. }
  254. return [
  255. floor($width * $ratio),
  256. floor($height * $ratio)
  257. ];
  258. }
  259. }