diff --git a/lib/nulla/uploader.ex b/lib/nulla/uploader.ex new file mode 100644 index 0000000..980d838 --- /dev/null +++ b/lib/nulla/uploader.ex @@ -0,0 +1,91 @@ +defmodule Nulla.Uploader do + alias Nulla.Snowflake + alias Nulla.MediaAttachments + + @upload_base "priv/static/system" + + def upload(%Plug.Upload{path: temp_path, filename: original_name}, domain, name, note_id) do + {:ok, binary} = File.read(temp_path) + ext = Path.extname(original_name) + mimetype = MIME.type(ext) + filename = Base.encode16(:crypto.strong_rand_bytes(8), case: :lower) <> ext + media_attachment_id = Snowflake.next_id() + + relative_path = + Path.join([ + "media_attachments/files", + partition_id(media_attachment_id), + "original", + filename + ]) + + full_path = Path.join(@upload_base, relative_path) + + full_path |> Path.dirname() |> File.mkdir_p!() + + File.write!(full_path, binary) + + type = + cond do + mimetype =~ "image" -> "Image" + mimetype =~ "video" -> "Video" + mimetype =~ "audio" -> "Audio" + true -> "Document" + end + + {width, height} = get_width_and_height(temp_path, mimetype) + + MediaAttachments.create_media_attachment(%{ + id: media_attachment_id, + type: type, + mediaType: mimetype, + url: "https://#{domain}/#{relative_path}", + name: name, + width: width, + height: height, + note_id: note_id + }) + end + + defp get_width_and_height(path, mimetype) do + cond do + mimetype =~ "image" or "video" -> + {output, 0} = + System.cmd("ffprobe", [ + "-v", + "error", + "-select_streams", + "v:0", + "-show_entries", + "stream=width,height", + "-of", + "default=noprint_wrappers=1", + path + ]) + + width = + Regex.run(~r/width=(\d+)/, output) + |> List.last() + |> String.to_integer() + + height = + Regex.run(~r/height=(\d+)/, output) + |> List.last() + |> String.to_integer() + + {width, height} + + true -> + {nil, nil} + end + end + + defp partition_id(id) do + id + |> Integer.to_string() + |> String.pad_leading(18, "0") + |> String.graphemes() + |> Enum.chunk_every(3) + |> Enum.map_join("/", &Enum.join/1) + end +end