This commit is contained in:
Roman Chvanikov 2020-12-07 22:37:56 +03:00
parent b3ee618e0d
commit 193c17cea5
7 changed files with 197 additions and 13 deletions

88
lib/pleroma/media.ex Normal file
View File

@ -0,0 +1,88 @@
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
%Media{}
|> changeset(%{
href: url["href"],
type: url["type"],
media_type: url["mediaType"],
name: data["name"],
blurhash: nil,
meta: %{},
user_id: user.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
}
end
end

View File

@ -119,6 +119,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)
@ -161,6 +162,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
defp maybe_update_media(%Object{data: %{"attachment" => []}}), do: :ok
defp maybe_update_media(%Object{id: id, data: %{"attachment" => attachments}}) do
Enum.each(attachments, fn %{"id" => media_id} ->
media_id
|> Pleroma.Media.get_by_id()
|> Pleroma.Media.update(%{object_id: id})
end)
end
defp insert_activity_with_expiration(data, local, recipients) do
struct = %Activity{
data: data,
@ -1190,10 +1201,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

View File

@ -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)

View File

@ -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

View File

@ -242,8 +242,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
true -> CommonAPI.thread_muted?(opts[:for], activity)
end
attachment_data = object.data["attachment"] || []
attachments = render_many(attachment_data, StatusView, "attachment.json", as: :attachment)
attachment_data = object.data["attachments"] || []
# attachments = render_many(attachment_data, StatusView, "attachment.json", as: :attachment)
attachments = render_many(attachment_data, StatusView, "media.json", as: :media)
created_at = Utils.to_masto_date(object.data["published"])
@ -436,6 +437,32 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
}
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

View File

@ -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),

View File

@ -0,0 +1,19 @@
defmodule Pleroma.Repo.Migrations.CreateMedia do
use Ecto.Migration
def change do
create_if_not_exists table(:media) do
add(:href, :string, null: false)
add(:type, :string, null: false)
add(:media_type, :string, null: false)
add(:name, :string)
add(:blurhash, :string)
add(:meta, :map)
# TODO discuss delete_all option
add(:object_id, references(:objects, on_delete: :nothing), null: true)
add(:user_id, references(:users, type: :uuid, on_delete: :nothing), null: false)
timestamps()
end
end
end