@@ -750,13 +750,40 @@ defmodule Algora.Accounts do
750
750
String . contains? ( url , "youtube.com" ) or String . contains? ( url , "youtu.be" )
751
751
end
752
752
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
+
753
774
def create_user_media ( % User { } = user , attrs ) do
754
775
if youtube_url? ( attrs [ "url" ] ) do
755
776
% 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
+ )
757
783
|> Repo . insert ( )
758
784
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" ] ) ,
760
787
object_path = media_object_path ( user . id , body ) ,
761
788
{ :ok , _ } <-
762
789
Algora.S3 . upload ( body , object_path ,
@@ -766,25 +793,62 @@ defmodule Algora.Accounts do
766
793
s3_url = Path . join ( Algora.S3 . bucket_url ( ) , object_path )
767
794
768
795
% 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
+ )
770
802
|> Repo . insert ( )
771
803
else
804
+ { :ok , % { status: status } } ->
805
+ Logger . error ( "Failed to process media: #{ inspect ( status ) } " )
806
+ { :error , status }
807
+
772
808
error ->
773
809
Logger . error ( "Failed to process media: #{ inspect ( error ) } " )
774
810
{ :error , :media_processing_failed }
775
811
end
776
812
end
777
813
end
778
814
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
+
779
823
defp media_object_path ( user_id , body ) do
780
824
hash = :md5 |> :crypto . hash ( body ) |> Base . encode16 ( case: :lower )
781
825
Path . join ( [ "media" , to_string ( user_id ) , hash ] )
782
826
end
783
827
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
788
852
end
789
853
790
854
defp extract_content_type ( headers ) do
0 commit comments