Skip to content

Commit f02b091

Browse files
committed
feat: improve user media management and seeding
1 parent 0bc5871 commit f02b091

File tree

4 files changed

+263
-68
lines changed

4 files changed

+263
-68
lines changed

lib/algora/accounts/accounts.ex

Lines changed: 71 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -750,13 +750,40 @@ defmodule Algora.Accounts do
750750
String.contains?(url, "youtube.com") or String.contains?(url, "youtu.be")
751751
end
752752

753+
def canonicalize_youtube_url(url) do
754+
cond do
755+
# Handle youtu.be URLs
756+
String.contains?(url, "youtu.be/") ->
757+
id = url |> String.split("youtu.be/") |> List.last() |> String.split("?") |> List.first()
758+
"https://youtube.com/embed/#{id}"
759+
760+
# Handle youtube.com/watch?v= URLs
761+
String.contains?(url, "youtube.com/watch?v=") ->
762+
id = url |> String.split("v=") |> List.last() |> String.split("&") |> List.first()
763+
"https://youtube.com/embed/#{id}"
764+
765+
# Handle youtube.com/embed/ URLs
766+
String.contains?(url, "youtube.com/embed/") ->
767+
"https://youtube.com/embed/#{url |> String.split("embed/") |> List.last()}"
768+
769+
true ->
770+
url
771+
end
772+
end
773+
753774
def create_user_media(%User{} = user, attrs) do
754775
if youtube_url?(attrs["url"]) do
755776
%UserMedia{}
756-
|> UserMedia.changeset(Map.put(attrs, "user_id", user.id))
777+
|> UserMedia.changeset(
778+
attrs
779+
|> Map.put("user_id", user.id)
780+
|> Map.put("original_url", attrs["url"])
781+
|> Map.update!("url", &canonicalize_youtube_url/1)
782+
)
757783
|> Repo.insert()
758784
else
759-
with {:ok, %{body: body, headers: headers}} <- fetch_media(attrs["url"]),
785+
with {:ok, %{body: body, headers: headers, status: status}} when status in 200..299 <-
786+
fetch_media(attrs["url"]),
760787
object_path = media_object_path(user.id, body),
761788
{:ok, _} <-
762789
Algora.S3.upload(body, object_path,
@@ -766,25 +793,62 @@ defmodule Algora.Accounts do
766793
s3_url = Path.join(Algora.S3.bucket_url(), object_path)
767794

768795
%UserMedia{}
769-
|> UserMedia.changeset(attrs |> Map.put("user_id", user.id) |> Map.put("url", s3_url))
796+
|> UserMedia.changeset(
797+
attrs
798+
|> Map.put("user_id", user.id)
799+
|> Map.put("original_url", attrs["url"])
800+
|> Map.put("url", s3_url)
801+
)
770802
|> Repo.insert()
771803
else
804+
{:ok, %{status: status}} ->
805+
Logger.error("Failed to process media: #{inspect(status)}")
806+
{:error, status}
807+
772808
error ->
773809
Logger.error("Failed to process media: #{inspect(error)}")
774810
{:error, :media_processing_failed}
775811
end
776812
end
777813
end
778814

815+
def fetch_or_create_user_media(%User{} = user, attrs) do
816+
# TODO: persist and compare against original URL
817+
case Repo.fetch_by(UserMedia, user_id: user.id, original_url: attrs["url"]) do
818+
{:ok, media} -> {:ok, media}
819+
_ -> create_user_media(user, attrs)
820+
end
821+
end
822+
779823
defp media_object_path(user_id, body) do
780824
hash = :md5 |> :crypto.hash(body) |> Base.encode16(case: :lower)
781825
Path.join(["media", to_string(user_id), hash])
782826
end
783827

784-
defp fetch_media(url) do
785-
:get
786-
|> Finch.build(url)
787-
|> Finch.request(Algora.Finch)
828+
def fetch_media(url, redirect_count \\ 0) do
829+
if redirect_count > 3 do
830+
{:error, :too_many_redirects}
831+
else
832+
case :get |> Finch.build(url) |> Finch.request(Algora.Finch) do
833+
{:ok, %{status: status, headers: headers}} when status in 300..399 ->
834+
case List.keyfind(headers, "location", 0) do
835+
{_, location} -> fetch_media(location, redirect_count + 1)
836+
nil -> {:error, :missing_redirect_location}
837+
end
838+
839+
{:ok, %{status: status}} when status in 400..499 ->
840+
new_url = url |> URI.parse() |> Map.put(:query, nil) |> to_string()
841+
842+
if new_url == url do
843+
{:error, status}
844+
else
845+
fetch_media(new_url, redirect_count + 1)
846+
end
847+
848+
other ->
849+
other
850+
end
851+
end
788852
end
789853

790854
defp extract_content_type(headers) do

lib/algora/accounts/schemas/user_media.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ defmodule Algora.Accounts.UserMedia do
66

77
typed_schema "user_media" do
88
field :url, :string
9+
field :original_url, :string
910

1011
belongs_to :user, User
1112

@@ -14,7 +15,7 @@ defmodule Algora.Accounts.UserMedia do
1415

1516
def changeset(user_media, attrs) do
1617
user_media
17-
|> cast(attrs, [:url, :user_id])
18+
|> cast(attrs, [:url, :original_url, :user_id])
1819
|> validate_required([:url, :user_id])
1920
|> generate_id()
2021
|> foreign_key_constraint(:user_id)

0 commit comments

Comments
 (0)