diff --git a/lib/ret/media_resolver.ex b/lib/ret/media_resolver.ex index 31e91f606..015cb554f 100644 --- a/lib/ret/media_resolver.ex +++ b/lib/ret/media_resolver.ex @@ -20,12 +20,14 @@ defmodule Ret.MediaResolver do @youtube_rate_limit %{scale: 8_000, limit: 1} @sketchfab_rate_limit %{scale: 60_000, limit: 15} + @icosa_rate_limit %{scale: 60_000, limit: 1000} @max_await_for_rate_limit_s 120 @non_video_root_hosts [ "sketchfab.com", "giphy.com", - "tenor.com" + "tenor.com", + "icosa.gallery" ] @deviant_id_regex ~r/\"DeviantArt:\/\/deviation\/([^"]+)/ @@ -79,6 +81,13 @@ defmodule Ret.MediaResolver do |> resolved()} end + # Necessary short circuit around google.com root_host to skip YT-DL check for Poly + def resolve(%MediaResolverQuery{url: %URI{host: "api.icosa.gallery"}} = query, root_host) do + rate_limited_resolve(query, root_host, @icosa_rate_limit, fn -> + resolve_non_video(query, root_host) + end) + end + def resolve(%MediaResolverQuery{} = query, root_host) when root_host in @non_video_root_hosts do resolve_non_video(query, root_host) end @@ -327,6 +336,44 @@ defmodule Ret.MediaResolver do {:commit, (resolved_url || uri) |> resolved(meta)} end + defp resolve_non_video( + %MediaResolverQuery{url: %URI{host: "api.icosa.gallery", path: "/v1/assets/" <> asset_id} = uri}, + "icosa.gallery" + ) do + # Increment stat for the request + Statix.increment("ret.media_resolver.icosa.requests") + + # Make the API call to get the asset data + payload = + "https://api.icosa.gallery/v1/assets/#{asset_id}" + |> retry_get_until_success() # Assuming this function sends the request and handles retries + |> Map.get(:body) + |> Poison.decode!() + + # Create the meta information based on the payload + meta = + %{ + expected_content_type: "model/gltf", + name: payload["displayName"], + author: payload["authorName"], + license: payload["license"] + } + + # Extract the GLTF2 format URL from the payload + uri = + payload["formats"] + |> Enum.find(&(&1["formatType"] == "GLTF2")) + |> Kernel.get_in(["root", "url"]) + |> URI.parse() + + # Increment stat for successful resolution + Statix.increment("ret.media_resolver.icosa.ok") + + # Return the URI and meta data for further processing + {:commit, resolved(uri, meta)} + end + + defp resolve_non_video( %MediaResolverQuery{url: %URI{path: "/models/" <> model_id}} = query, "sketchfab.com" = root_host diff --git a/lib/ret/media_search.ex b/lib/ret/media_search.ex index 03a4f615c..54de43ce9 100644 --- a/lib/ret/media_search.ex +++ b/lib/ret/media_search.ex @@ -249,6 +249,41 @@ defmodule Ret.MediaSearch do sketchfab_search(query) end + def search(%Ret.MediaSearchQuery{source: "icosa", cursor: cursor, filter: filter, q: q}) do + query = + URI.encode_query( + pageSize: @page_size, + maxComplexity: :MEDIUM, + format: :GLTF2, + pageToken: cursor, + category: filter, + keywords: q + ) + + res = + "https://api.icosa.gallery/v1/assets?#{query}" + |> retry_get_until_success() + + case res do + :error -> + :error + + res -> + decoded_res = res |> Map.get(:body) |> Poison.decode!() + entries = decoded_res |> Map.get("assets") |> Enum.map(&icosa_api_result_to_entry/1) + next_cursor = decoded_res |> Map.get("nextPageToken") + + {:commit, + %Ret.MediaSearchResult{ + meta: %Ret.MediaSearchResultMeta{ + next_cursor: next_cursor, + source: :icosa + }, + entries: entries + }} + end + end + def search(%Ret.MediaSearchQuery{source: "youtube_videos", cursor: cursor, filter: filter, q: q}) do with api_key when is_binary(api_key) <- resolver_config(:youtube_api_key) do query = @@ -377,6 +412,7 @@ defmodule Ret.MediaSearch do end end + def available?(:icosa), do: true # Icosa does not currently require an API key def available?(:bing_images), do: has_resolver_config?(:bing_search_api_key) def available?(:bing_videos), do: has_resolver_config?(:bing_search_api_key) def available?(:youtube_videos), do: has_resolver_config?(:youtube_api_key) @@ -957,6 +993,18 @@ defmodule Ret.MediaSearch do } end + defp icosa_api_result_to_entry(result) do + %{ + id: result["assetId"], + type: "icosa_model", + name: result["displayName"], + attributions: %{creator: %{name: result["authorName"]}}, + url: "http://api.icosa.gallery/v1/assets/" <> result["assetId"], + # url: result["url"], + images: %{preview: %{url: result["thumbnail"]["url"]}} + } + end + defp youtube_api_result_to_entry(result) do %{ id: result["id"]["videoId"], diff --git a/lib/ret/meta.ex b/lib/ret/meta.ex index adc310317..be3df373e 100644 --- a/lib/ret/meta.ex +++ b/lib/ret/meta.ex @@ -44,6 +44,7 @@ defmodule Ret.Meta do def available_integrations_meta do %{ twitter: Ret.TwitterClient.available?(), + icosa: Ret.MediaSearch.available?(:icosa), bing_images: Ret.MediaSearch.available?(:bing_images), bing_videos: Ret.MediaSearch.available?(:bing_videos), youtube_videos: Ret.MediaSearch.available?(:youtube_videos), diff --git a/lib/ret_web/controllers/api/v1/media_search_controller.ex b/lib/ret_web/controllers/api/v1/media_search_controller.ex index f21ca1460..946eaad61 100644 --- a/lib/ret_web/controllers/api/v1/media_search_controller.ex +++ b/lib/ret_web/controllers/api/v1/media_search_controller.ex @@ -98,6 +98,7 @@ defmodule RetWeb.Api.V1.MediaSearchController do def index(conn, %{"source" => source} = params) when source in [ "sketchfab", + "icosa", "tenor", "youtube_videos", "bing_videos", @@ -127,7 +128,7 @@ defmodule RetWeb.Api.V1.MediaSearchController do # For Google services, increase cache duration for landing pages by using long-lived cache, due to quotas. defp cache_for_query(%Ret.MediaSearchQuery{source: source, q: nil}) - when source == "youtube_videos", + when source == "youtube_videos" or source == "icosa", do: :media_search_results_long defp cache_for_query(_query), do: :media_search_results