From 333389607f4b3f7f866cc0a7aaf16a5c2de0ef00 Mon Sep 17 00:00:00 2001 From: lain Date: Sat, 24 Mar 2018 19:14:28 +0100 Subject: [PATCH] Basic peertube support. --- .../web/activity_pub/transmogrifier.ex | 41 ++++++++++++++++++- lib/pleroma/web/activity_pub/utils.ex | 11 +++-- .../representers/activity_representer.ex | 11 ++++- .../representers/object_representer.ex | 4 +- test/fixtures/httpoison_mock/7even.json | 1 + .../httpoison_mock/peertube.moe-vid.json | 1 + test/support/httpoison_mock.ex | 14 +++++++ test/web/activity_pub/activity_pub_test.exs | 6 +++ 8 files changed, 80 insertions(+), 9 deletions(-) create mode 100644 test/fixtures/httpoison_mock/7even.json create mode 100644 test/fixtures/httpoison_mock/peertube.moe-vid.json diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 61631e1ea..5c469c901 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -7,17 +7,25 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do alias Pleroma.Activity alias Pleroma.Repo alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.ActivityPub.Utils import Ecto.Query require Logger + def get_actor(%{"actor" => actor}) when is_binary(actor), do: actor + def get_actor(%{"actor" => actor_list}) do + Enum.find(actor_list, fn(%{"type" => type}) -> type == "Person" end) + |> Map.get("id") + end + @doc """ Modifies an incoming AP object (mastodon format) to our internal format. """ def fix_object(object) do + attributedTo = object["attributedTo"] object - |> Map.put("actor", object["attributedTo"]) + |> Map.put("actor", get_actor(%{"actor" => attributedTo})) |> fix_attachments |> fix_context |> fix_in_reply_to @@ -42,8 +50,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def fix_in_reply_to(object), do: object def fix_context(object) do + context = object["context"] || object["conversation"] || Utils.generate_context_id object - |> Map.put("context", object["conversation"]) + |> Map.put("context", context) + |> Map.put("conversation", context) end def fix_attachments(object) do @@ -173,6 +183,33 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do end end + def handle_incoming(%{"type" => "Create", "object" => %{"type" => "Video"} = object} = data) do + actor = get_actor(data) + data = Map.put(data, "actor", actor) + with nil <- Activity.get_create_activity_by_object_ap_id(object["id"]), + %User{} = user <- User.get_or_fetch_by_ap_id(data["actor"]) do + + object = fix_object(data["object"]) + + params = %{ + to: data["to"], + object: object, + actor: user, + context: object["conversation"], + local: false, + published: data["published"], + additional: Map.take(data, [ + "cc", + "id" + ]) + } + + ActivityPub.create(params) + else + %Activity{} = activity -> {:ok, activity} + _e -> :error + end + end # TODO # Accept # Undo diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index a25b27aab..feb2f07a4 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -88,7 +88,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do @doc """ Inserts a full object if it is contained in an activity. """ - def insert_full_object(%{"object" => %{"type" => type} = object_data}) when is_map(object_data) and type in ["Note"] do + def insert_full_object(%{"object" => %{"type" => type} = object_data}) when is_map(object_data) and type in ["Note", "Video"] do with {:ok, _} <- Object.create(object_data) do :ok end @@ -150,13 +150,15 @@ defmodule Pleroma.Web.ActivityPub.Utils do end def add_like_to_object(%Activity{data: %{"actor" => actor}}, object) do - with likes <- [actor | (object.data["likes"] || [])] |> Enum.uniq do + likes = if is_list(object.data["likes"]), do: object.data["likes"], else: [] + with likes <- [actor | likes] |> Enum.uniq do update_likes_in_object(likes, object) end end def remove_like_from_object(%Activity{data: %{"actor" => actor}}, object) do - with likes <- (object.data["likes"] || []) |> List.delete(actor) do + likes = if is_list(object.data["likes"]), do: object.data["likes"], else: [] + with likes <- likes |> List.delete(actor) do update_likes_in_object(likes, object) end end @@ -207,7 +209,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do end def add_announce_to_object(%Activity{data: %{"actor" => actor}}, object) do - with announcements <- [actor | (object.data["announcements"] || [])] |> Enum.uniq do + announcements = if is_list(object.data["announcements"]), do: object.data["announcements"], else: [] + with announcements <- [actor | announcements] |> Enum.uniq do update_element_in_object("announcement", announcements, object) end end diff --git a/lib/pleroma/web/twitter_api/representers/activity_representer.ex b/lib/pleroma/web/twitter_api/representers/activity_representer.ex index 5199cef8e..ccd877cd5 100644 --- a/lib/pleroma/web/twitter_api/representers/activity_representer.ex +++ b/lib/pleroma/web/twitter_api/representers/activity_representer.ex @@ -117,6 +117,7 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do } end + # Handles "Note" and "Video" def to_map(%Activity{data: %{"object" => %{"content" => content} = object}} = activity, %{user: user} = opts) do created_at = object["published"] |> Utils.date_to_asctime like_count = object["like_count"] || 0 @@ -147,6 +148,14 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do html = HtmlSanitizeEx.basic_html(content) |> Formatter.emojify(object["emoji"]) + video = if object["type"] == "Video" do + vid = [object] + else + [] + end + + attachments = (object["attachment"] || []) ++ video + %{ "id" => activity.id, "uri" => activity.data["object"]["id"], @@ -158,7 +167,7 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do "created_at" => created_at, "in_reply_to_status_id" => object["inReplyToStatusId"], "statusnet_conversation_id" => conversation_id, - "attachments" => (object["attachment"] || []) |> ObjectRepresenter.enum_to_list(opts), + "attachments" => attachments |> ObjectRepresenter.enum_to_list(opts), "attentions" => attentions, "fave_num" => like_count, "repeat_num" => announcement_count, diff --git a/lib/pleroma/web/twitter_api/representers/object_representer.ex b/lib/pleroma/web/twitter_api/representers/object_representer.ex index e2d653ba8..3c4215a2c 100644 --- a/lib/pleroma/web/twitter_api/representers/object_representer.ex +++ b/lib/pleroma/web/twitter_api/representers/object_representer.ex @@ -6,7 +6,7 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ObjectRepresenter do data = object.data %{ url: url["href"] |> Pleroma.Web.MediaProxy.url(), - mimetype: url["mediaType"], + mimetype: url["mediaType"] || url["mimeType"], id: data["uuid"], oembed: false } @@ -15,7 +15,7 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ObjectRepresenter do def to_map(%Object{data: %{"url" => url} = data}, _opts) when is_binary(url) do %{ url: url |> Pleroma.Web.MediaProxy.url(), - mimetype: data["mediaType"], + mimetype: data["mediaType"] || url["mimeType"], id: data["uuid"], oembed: false } diff --git a/test/fixtures/httpoison_mock/7even.json b/test/fixtures/httpoison_mock/7even.json new file mode 100644 index 000000000..eb3bab14e --- /dev/null +++ b/test/fixtures/httpoison_mock/7even.json @@ -0,0 +1 @@ +{"type":"Person","id":"https://peertube.moe/accounts/7even","following":"https://peertube.moe/accounts/7even/following","followers":"https://peertube.moe/accounts/7even/followers","inbox":"https://peertube.moe/accounts/7even/inbox","outbox":"https://peertube.moe/accounts/7even/outbox","preferredUsername":"7even","url":"https://peertube.moe/accounts/7even","name":"7even","endpoints":{"sharedInbox":"https://peertube.moe/inbox"},"uuid":"fd6a914d-0383-4aca-b740-65ed96a0dd63","publicKey":{"id":"https://peertube.moe/accounts/7even#main-key","owner":"https://peertube.moe/accounts/7even","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3vQ2uvYbDprVrq9Ti2wB\nn2J0WkewrzR/+NF4+KVl9s+FMyE4jlmnz+9WEnacKhV1x8a3SrsjLgND55WxNaPj\nabrh6lWmI0SNmHBLi1BejIVAR7CZElF3yCxG0xtNna0Hg6bhtL6I61QxnClhFunu\nDO4i6uyrUu2iXWGQDPzpWkGFrmZdyHOCNIr5PekphR/wcCluwbndO51Ku2RJAblW\nI+QlDG7ailpXyTZGUEO5yfJZX7dkCET1AsNxeBo41aPYzUN5rgRCDB/AdJXxrgZb\nsmtiObB9u+KYk9DuegPpHP3y+dapCSCvStBdPyTaPzsi1y/pOiTVfaxw0NYEJ/Cs\n0QIDAQAB\n-----END PUBLIC KEY-----"},"icon":{"type":"Image","mediaType":"image/png","url":"https://peertube.moe/static/avatars/0815978b-dd71-4797-ae3f-493a3445882a.png"},"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"RsaSignature2017":"https://w3id.org/security#RsaSignature2017","Hashtag":"as:Hashtag","uuid":"http://schema.org/identifier","category":"http://schema.org/category","licence":"http://schema.org/license","sensitive":"as:sensitive","language":"http://schema.org/inLanguage","views":"http://schema.org/Number","size":"http://schema.org/Number","commentsEnabled":"http://schema.org/Boolean","support":"http://schema.org/Text"},{"likes":{"@id":"as:likes","@type":"@id"},"dislikes":{"@id":"as:dislikes","@type":"@id"},"shares":{"@id":"as:shares","@type":"@id"},"comments":{"@id":"as:comments","@type":"@id"}}],"summary":null} \ No newline at end of file diff --git a/test/fixtures/httpoison_mock/peertube.moe-vid.json b/test/fixtures/httpoison_mock/peertube.moe-vid.json new file mode 100644 index 000000000..76296eb7d --- /dev/null +++ b/test/fixtures/httpoison_mock/peertube.moe-vid.json @@ -0,0 +1 @@ +{"type":"Video","id":"https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3","name":"Friday Night","duration":"PT29S","uuid":"df5f464b-be8d-46fb-ad81-2d4c2d1630e3","tag":[{"type":"Hashtag","name":"feels"}],"views":12,"sensitive":false,"commentsEnabled":true,"published":"2018-03-23T16:43:22.988Z","updated":"2018-03-24T16:28:46.002Z","mediaType":"text/markdown","content":"tfw\r\n\r\n\r\nsong is 'my old piano' by diana ross","support":null,"icon":{"type":"Image","url":"https://peertube.moe/static/thumbnails/df5f464b-be8d-46fb-ad81-2d4c2d1630e3.jpg","mediaType":"image/jpeg","width":200,"height":110},"url":[{"type":"Link","mimeType":"video/mp4","href":"https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4","width":480,"size":5015880},{"type":"Link","mimeType":"application/x-bittorrent","href":"https://peertube.moe/static/torrents/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.torrent","width":480},{"type":"Link","mimeType":"application/x-bittorrent;x-scheme-handler/magnet","href":"magnet:?xs=https%3A%2F%2Fpeertube.moe%2Fstatic%2Ftorrents%2Fdf5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.torrent&xt=urn:btih:11d3af6b5c812a376c2b29cdbd46e5fb42ee730e&dn=Friday+Night&tr=wss%3A%2F%2Fpeertube.moe%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube.moe%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube.moe%2Fstatic%2Fwebseed%2Fdf5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4","width":480},{"type":"Link","mimeType":"video/mp4","href":"https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-360.mp4","width":360,"size":3620040},{"type":"Link","mimeType":"application/x-bittorrent","href":"https://peertube.moe/static/torrents/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-360.torrent","width":360},{"type":"Link","mimeType":"application/x-bittorrent;x-scheme-handler/magnet","href":"magnet:?xs=https%3A%2F%2Fpeertube.moe%2Fstatic%2Ftorrents%2Fdf5f464b-be8d-46fb-ad81-2d4c2d1630e3-360.torrent&xt=urn:btih:1c3885b4d7cdb46193b62b9b76e72b1409cfb297&dn=Friday+Night&tr=wss%3A%2F%2Fpeertube.moe%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube.moe%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube.moe%2Fstatic%2Fwebseed%2Fdf5f464b-be8d-46fb-ad81-2d4c2d1630e3-360.mp4","width":360},{"type":"Link","mimeType":"video/mp4","href":"https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-240.mp4","width":240,"size":2305488},{"type":"Link","mimeType":"application/x-bittorrent","href":"https://peertube.moe/static/torrents/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-240.torrent","width":240},{"type":"Link","mimeType":"application/x-bittorrent;x-scheme-handler/magnet","href":"magnet:?xs=https%3A%2F%2Fpeertube.moe%2Fstatic%2Ftorrents%2Fdf5f464b-be8d-46fb-ad81-2d4c2d1630e3-240.torrent&xt=urn:btih:ac5773352d9e26f982d2da63acfb244f01ccafa4&dn=Friday+Night&tr=wss%3A%2F%2Fpeertube.moe%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube.moe%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube.moe%2Fstatic%2Fwebseed%2Fdf5f464b-be8d-46fb-ad81-2d4c2d1630e3-240.mp4","width":240},{"type":"Link","mimeType":"video/mp4","href":"https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-720.mp4","width":720,"size":7928231},{"type":"Link","mimeType":"application/x-bittorrent","href":"https://peertube.moe/static/torrents/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-720.torrent","width":720},{"type":"Link","mimeType":"application/x-bittorrent;x-scheme-handler/magnet","href":"magnet:?xs=https%3A%2F%2Fpeertube.moe%2Fstatic%2Ftorrents%2Fdf5f464b-be8d-46fb-ad81-2d4c2d1630e3-720.torrent&xt=urn:btih:b591068f4533c4e2865bb4cbb89887aecccdc523&dn=Friday+Night&tr=wss%3A%2F%2Fpeertube.moe%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube.moe%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube.moe%2Fstatic%2Fwebseed%2Fdf5f464b-be8d-46fb-ad81-2d4c2d1630e3-720.mp4","width":720},{"type":"Link","mimeType":"text/html","href":"https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"}],"likes":{"id":"https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3/likes","type":"OrderedCollection","totalItems":0,"orderedItems":[]},"dislikes":{"id":"https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3/dislikes","type":"OrderedCollection","totalItems":0,"orderedItems":[]},"shares":{"id":"https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3/announces","type":"OrderedCollection","totalItems":2,"orderedItems":["https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3/announces/465","https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3/announces/1"]},"comments":{"id":"https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3/comments","type":"OrderedCollection","totalItems":0,"orderedItems":[]},"attributedTo":[{"type":"Group","id":"https://peertube.moe/video-channels/5224869f-aa63-4c83-ab3a-87c3a5ac440e"},{"type":"Person","id":"https://peertube.moe/accounts/7even"}],"to":["https://www.w3.org/ns/activitystreams#Public"],"cc":[],"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"RsaSignature2017":"https://w3id.org/security#RsaSignature2017","Hashtag":"as:Hashtag","uuid":"http://schema.org/identifier","category":"http://schema.org/category","licence":"http://schema.org/license","sensitive":"as:sensitive","language":"http://schema.org/inLanguage","views":"http://schema.org/Number","size":"http://schema.org/Number","commentsEnabled":"http://schema.org/Boolean","support":"http://schema.org/Text"},{"likes":{"@id":"as:likes","@type":"@id"},"dislikes":{"@id":"as:dislikes","@type":"@id"},"shares":{"@id":"as:shares","@type":"@id"},"comments":{"@id":"as:comments","@type":"@id"}}]} \ No newline at end of file diff --git a/test/support/httpoison_mock.ex b/test/support/httpoison_mock.ex index c70598691..48dfa7d67 100644 --- a/test/support/httpoison_mock.ex +++ b/test/support/httpoison_mock.ex @@ -456,6 +456,20 @@ defmodule HTTPoisonMock do }} end + def get("https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3", _, _) do + {:ok, %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/peertube.moe-vid.json") + }} + end + + def get("https://peertube.moe/accounts/7even", _, _) do + {:ok, %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/7even.json") + }} + end + def get(url, body, headers) do {:error, "Not implemented the mock response for get #{inspect(url)}, #{inspect(body)}, #{inspect(headers)}"} end diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index 96792bca5..1a5203a09 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -353,6 +353,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do end end + test "it can fetch peertube videos" do + {:ok, object} = ActivityPub.fetch_object_from_id("https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3") + + assert object + end + def data_uri do File.read!("test/fixtures/avatar_data_uri") end