diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/pleroma/media.ex | 92 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/activity_pub.ex | 20 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/activity_pub_controller.ex | 11 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/transmogrifier.ex | 35 | ||||
-rw-r--r-- | lib/pleroma/web/common_api/utils.ex | 10 | ||||
-rw-r--r-- | lib/pleroma/web/mastodon_api/controllers/media_controller.ex | 42 | ||||
-rw-r--r-- | lib/pleroma/web/mastodon_api/views/status_view.ex | 56 | ||||
-rw-r--r-- | lib/pleroma/web/plugs/uploaded_media.ex | 2 |
8 files changed, 242 insertions, 26 deletions
diff --git a/lib/pleroma/media.ex b/lib/pleroma/media.ex new file mode 100644 index 000000000..431c06bb5 --- /dev/null +++ b/lib/pleroma/media.ex @@ -0,0 +1,92 @@ +defmodule Pleroma.Media do + use Ecto.Schema + + import Ecto.Changeset + + alias Pleroma.Media + alias Pleroma.Repo + alias Pleroma.User + + @derive {Jason.Encoder, + only: [:href, :type, :media_type, :name, :blurhash, :meta, :object_id, :user_id]} + + @type t() :: %__MODULE__{} + + schema "media" do + field(:href, :string) + field(:type, :string) + field(:media_type, :string) + field(:name, :string) + field(:blurhash, :string) + field(:meta, :map) + + belongs_to(:object, Pleroma.Object) + belongs_to(:user, Pleroma.User, type: FlakeId.Ecto.CompatType) + + timestamps() + end + + def create_from_object_data(%{"url" => [url]} = data, %{user: user} = opts) do + object_id = get_in(opts, [:object, "id"]) + + %Media{} + |> changeset(%{ + href: url["href"], + type: url["type"], + media_type: url["mediaType"], + name: data["name"], + blurhash: nil, + meta: %{}, + user_id: user.id, + object_id: object_id + }) + |> Repo.insert() + end + + def get_by_id(nil), do: nil + def get_by_id(id), do: Repo.get(Media, id) + + @spec authorize_access(Media.t(), User.t()) :: :ok | {:error, :forbidden} + def authorize_access(%Media{user_id: user_id}, %User{id: user_id}), do: :ok + def authorize_access(%Media{user_id: user_id}, %User{id: user_id}), do: {:error, :forbidden} + + def update(%Media{} = media, attrs \\ %{}) do + media + |> changeset(attrs) + |> Repo.update() + end + + def from_object(%Pleroma.Object{data: data}, %{user: user}) do + %Media{href: data["href"], user_id: user.id} + end + + def insert(%Media{} = media) do + media + |> changeset() + |> Repo.insert() + end + + def changeset(struct, params \\ %{}) do + struct + |> cast(params, [:href, :type, :media_type, :name, :blurhash, :meta, :user_id, :object_id]) + |> validate_required([:href, :type, :media_type]) + end + + def to_object_form(%Media{} = media) do + %{ + "id" => media.id, + "url" => [ + %{ + "href" => media.href, + "type" => media.type, + "mediaType" => media.media_type + } + ], + "name" => media.name, + "type" => "Document", + "blurhash" => media.blurhash, + "mediaType" => media.media_type, + "actor" => User.get_by_id(media.user_id).ap_id + } + end +end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index c5bc08153..c2948fd52 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -123,6 +123,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do {:fake, false, map, recipients} <- {:fake, fake, map, recipients}, {:containment, :ok} <- {:containment, Containment.contain_child(map)}, {:ok, map, object} <- insert_full_object(map), + :ok <- maybe_update_media(object), {:ok, activity} <- insert_activity_with_expiration(map, local, recipients) do # Splice in the child object if we have one. activity = Maps.put_if_present(activity, :object, object) @@ -165,6 +166,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end + defp maybe_update_media(%Object{data: %{"attachment" => []}}), do: :ok + + defp maybe_update_media(%Object{data: %{"id" => id, "attachment" => attachments}}) do + Enum.each(attachments, fn data -> + with %{"id" => media_id} <- data do + media_id + |> Pleroma.Media.get_by_id() + |> Pleroma.Media.update(%{object_id: id}) + end + end) + end + defp insert_activity_with_expiration(data, local, recipients) do struct = %Activity{ data: data, @@ -1200,10 +1213,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do @spec upload(Upload.source(), keyword()) :: {:ok, Object.t()} | {:error, any()} def upload(file, opts \\ []) do - with {:ok, data} <- Upload.store(file, opts) do - obj_data = Maps.put_if_present(data, "actor", opts[:actor]) - - Repo.insert(%Object{data: obj_data}) + with {:ok, data} <- Upload.store(file, opts), + %User{} <- opts[:user] do + Pleroma.Media.create_from_object_data(data, %{user: opts[:user]}) end end diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index eb9e119f7..2e5069cb5 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -271,7 +271,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do end end - def inbox(%{assigns: %{valid_signature: true}} = conn, %{"nickname" => nickname} = params) do + def inbox(conn, params) do + IO.inspect(%{conn: conn, params: params}) + inbox2(conn, params) + end + + def inbox2(%{assigns: %{valid_signature: true}} = conn, %{"nickname" => nickname} = params) do with %User{} = recipient <- User.get_cached_by_nickname(nickname), {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(params["actor"]), true <- Utils.recipient_in_message(recipient, actor, params), @@ -281,13 +286,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do end end - def inbox(%{assigns: %{valid_signature: true}} = conn, params) do + def inbox2(%{assigns: %{valid_signature: true}} = conn, params) do Federator.incoming_ap_doc(params) json(conn, "ok") end # POST /relay/inbox -or- POST /internal/fetch/inbox - def inbox(conn, params) do + def inbox2(conn, params) do if params["type"] == "Create" && FederatingPlug.federating?() do post_inbox_relayed_create(conn, params) else diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 4d9a5617e..7f7387c31 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do alias Pleroma.Activity alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.Maps + alias Pleroma.Media alias Pleroma.Object alias Pleroma.Object.Containment alias Pleroma.Repo @@ -32,18 +33,19 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do """ def fix_object(object, options \\ []) do object - |> strip_internal_fields - |> fix_actor - |> fix_url - |> fix_attachments - |> fix_context + |> strip_internal_fields() + |> fix_actor() + |> fix_url() + |> fix_attachments() + |> fix_media() + |> fix_context() |> fix_in_reply_to(options) - |> fix_emoji - |> fix_tag - |> set_sensitive - |> fix_content_map - |> fix_addressing - |> fix_summary + |> fix_emoji() + |> fix_tag() + |> set_sensitive() + |> fix_content_map() + |> fix_addressing() + |> fix_summary() |> fix_type(options) end @@ -270,6 +272,17 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def fix_attachments(object), do: object + def fix_media(%{"attachment" => [_ | _] = attachments} = object) do + Enum.each( + attachments, + &Media.create_from_object_data(&1, %{user: User.get_by_ap_id(object.actor), object: object}) + ) + + object + end + + def fix_media(object), do: object + def fix_url(%{"url" => url} = object) when is_map(url) do Map.put(object, "url", url["href"]) end diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 9587dfa25..6a4e22f27 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -11,6 +11,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do alias Pleroma.Config alias Pleroma.Conversation.Participation alias Pleroma.Formatter + alias Pleroma.Media alias Pleroma.Object alias Pleroma.Repo alias Pleroma.User @@ -37,8 +38,8 @@ defmodule Pleroma.Web.CommonAPI.Utils do def attachments_from_ids_no_descs(ids) do Enum.map(ids, fn media_id -> - case Repo.get(Object, media_id) do - %Object{data: data} -> data + case Repo.get(Media, media_id) do + %Media{} = media -> Media.to_object_form(media) _ -> nil end end) @@ -51,8 +52,9 @@ defmodule Pleroma.Web.CommonAPI.Utils do {_, descs} = Jason.decode(descs_str) Enum.map(ids, fn media_id -> - with %Object{data: data} <- Repo.get(Object, media_id) do - Map.put(data, "name", descs[media_id]) + with %Media{} = media <- Repo.get(Media, media_id) do + %Media{media | name: descs[media_id]} + |> Media.to_object_form() end end) |> Enum.reject(&is_nil/1) diff --git a/lib/pleroma/web/mastodon_api/controllers/media_controller.ex b/lib/pleroma/web/mastodon_api/controllers/media_controller.ex index d6949ed80..1bd521460 100644 --- a/lib/pleroma/web/mastodon_api/controllers/media_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/media_controller.ex @@ -5,6 +5,7 @@ defmodule Pleroma.Web.MastodonAPI.MediaController do use Pleroma.Web, :controller + alias Pleroma.Media alias Pleroma.Object alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub @@ -22,6 +23,20 @@ defmodule Pleroma.Web.MastodonAPI.MediaController do @doc "POST /api/v1/media" def create(%{assigns: %{user: user}, body_params: %{file: file} = data} = conn, _) do + with {:ok, media} <- + ActivityPub.upload( + file, + user: user, + actor: User.ap_id(user), + description: Map.get(data, :description) + ) do + render(conn, "media.json", %{media: media}) + end + end + + def create(_conn, _data), do: {:error, :bad_request} + + def _create(%{assigns: %{user: user}, body_params: %{file: file} = data} = conn, _) do with {:ok, object} <- ActivityPub.upload( file, @@ -34,7 +49,7 @@ defmodule Pleroma.Web.MastodonAPI.MediaController do end end - def create(_conn, _data), do: {:error, :bad_request} + def _create(_conn, _data), do: {:error, :bad_request} @doc "POST /api/v2/media" def create2(%{assigns: %{user: user}, body_params: %{file: file} = data} = conn, _) do @@ -56,6 +71,18 @@ defmodule Pleroma.Web.MastodonAPI.MediaController do @doc "PUT /api/v1/media/:id" def update(%{assigns: %{user: user}, body_params: %{description: description}} = conn, %{id: id}) do + with %Media{} = media <- Media.get_by_id(id), + :ok <- Media.authorize_access(media, user), + {:ok, %Media{} = media} <- Media.update(media, %{"name" => description}) do + render(conn, "media.json", %{media: media}) + end + end + + def update(conn, data), do: show(conn, data) + + def _update(%{assigns: %{user: user}, body_params: %{description: description}} = conn, %{ + id: id + }) do with %Object{} = object <- Object.get_by_id(id), :ok <- Object.authorize_access(object, user), {:ok, %Object{data: data}} <- Object.update_data(object, %{"name" => description}) do @@ -65,10 +92,19 @@ defmodule Pleroma.Web.MastodonAPI.MediaController do end end - def update(conn, data), do: show(conn, data) + def _update(conn, data), do: show(conn, data) @doc "GET /api/v1/media/:id" def show(%{assigns: %{user: user}} = conn, %{id: id}) do + with %Pleroma.Media{} = media <- Pleroma.Media.get_by_id(id), + :ok <- Pleroma.Media.authorize_access(media, user) do + render(conn, "media.json", %{media: media}) + end + end + + def show(_conn, _data), do: {:error, :bad_request} + + def _show(%{assigns: %{user: user}} = conn, %{id: id}) do with %Object{data: data, id: object_id} = object <- Object.get_by_id(id), :ok <- Object.authorize_access(object, user) do attachment_data = Map.put(data, "id", object_id) @@ -77,5 +113,5 @@ defmodule Pleroma.Web.MastodonAPI.MediaController do end end - def show(_conn, _data), do: {:error, :bad_request} + def _show(_conn, _data), do: {:error, :bad_request} end diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index cd1a85088..8c6d56e0f 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -243,7 +243,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do end attachment_data = object.data["attachment"] || [] - attachments = render_many(attachment_data, StatusView, "attachment.json", as: :attachment) + # attachments = render_many(attachment_data, StatusView, "attachment.json", as: :attachment) + attachments = render_many(attachment_data, StatusView, "object_media.json", as: :media) created_at = Utils.to_masto_date(object.data["published"]) @@ -436,6 +437,59 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do } end + def render("object_media.json", %{media: media}) do + [url] = media["url"] + media_type = media["mediaType"] || "image" + href = MediaProxy.url(url["href"]) + href_preview = MediaProxy.preview_url(url["href"]) + + type = + cond do + String.contains?(media_type, "image") -> "image" + String.contains?(media_type, "video") -> "video" + String.contains?(media_type, "audio") -> "audio" + true -> "unknown" + end + + %{ + id: to_string(media["id"]), + url: href, + remote_url: href, + preview_url: href_preview, + text_url: href, + type: type, + description: media["name"], + pleroma: %{mime_type: media_type}, + blurhash: media["blurhash"] + } + end + + def render("media.json", %{media: media}) do + media_type = media.media_type || media.mime_type || "image" + href = MediaProxy.url(media.href) + href_preview = MediaProxy.preview_url(media.href) + + type = + cond do + String.contains?(media_type, "image") -> "image" + String.contains?(media_type, "video") -> "video" + String.contains?(media_type, "audio") -> "audio" + true -> "unknown" + end + + %{ + id: to_string(media.id), + url: href, + remote_url: href, + preview_url: href_preview, + text_url: href, + type: type, + description: media.name, + pleroma: %{mime_type: media_type}, + blurhash: media.blurhash + } + end + def render("context.json", %{activity: activity, activities: activities, user: user}) do %{ancestors: ancestors, descendants: descendants} = activities diff --git a/lib/pleroma/web/plugs/uploaded_media.ex b/lib/pleroma/web/plugs/uploaded_media.ex index 2378e98d2..0350fdfbf 100644 --- a/lib/pleroma/web/plugs/uploaded_media.ex +++ b/lib/pleroma/web/plugs/uploaded_media.ex @@ -46,6 +46,8 @@ defmodule Pleroma.Web.Plugs.UploadedMedia do config = Pleroma.Config.get(Pleroma.Upload) + # https://pleroma.local/media/cf61935ec407b4df8fd3dcf58352948eb6231bdfe12fcbf5270e653c20da9860.jpeg + with uploader <- Keyword.fetch!(config, :uploader), proxy_remote = Keyword.get(config, :proxy_remote, false), {:ok, get_method} <- uploader.get_file(file), |