logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma git clone https://hacktivis.me/git/pleroma.git
commit: 4d7c9f4410b702d26681c4cd3596c1a9857825b8
parent 454ac87ca4efc88bcdf60cd86f6b6a33785f11c1
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date:   Mon, 29 May 2023 12:09:11 +0200

Merge branch 'pleroma/remove-scrobbles'

Diffstat:

MCHANGELOG.md1+
Mdocs/development/API/pleroma_api.md32--------------------------------
Mlib/pleroma/web/activity_pub/activity_pub.ex20--------------------
Mlib/pleroma/web/activity_pub/transmogrifier.ex34+---------------------------------
Mlib/pleroma/web/activity_pub/utils.ex15---------------
Mlib/pleroma/web/activity_pub/views/object_view.ex3+--
Mlib/pleroma/web/api_spec.ex2+-
Dlib/pleroma/web/api_spec/operations/pleroma_scrobble_operation.ex102-------------------------------------------------------------------------------
Mlib/pleroma/web/common_api.ex6------
Mlib/pleroma/web/common_api/activity_draft.ex24------------------------
Dlib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex52----------------------------------------------------
Dlib/pleroma/web/pleroma_api/views/scrobble_view.ex37-------------------------------------
Mlib/pleroma/web/router.ex3---
Mtest/pleroma/web/activity_pub/activity_pub_test.exs36------------------------------------
Mtest/pleroma/web/activity_pub/transmogrifier/audio_handling_test.exs35-----------------------------------
Mtest/pleroma/web/activity_pub/transmogrifier_test.exs14--------------
Mtest/pleroma/web/common_api_test.exs39---------------------------------------
Dtest/pleroma/web/pleroma_api/controllers/scrobble_controller_test.exs60------------------------------------------------------------
Dtest/pleroma/web/pleroma_api/views/scrobble_view_test.exs20--------------------
Mtest/support/factory.ex19-------------------
20 files changed, 4 insertions(+), 550 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md @@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Removed - BREAKING: Support for passwords generated with `crypt(3)` (Gnu Social migration artifact) +- BREAKING: Support for audio scrobbling via `Listen` activities ## 2.5.2 diff --git a/docs/development/API/pleroma_api.md b/docs/development/API/pleroma_api.md @@ -576,38 +576,6 @@ The status posting endpoint takes an additional parameter, `in_reply_to_conversa * Response: the archive of the pack with a 200 status code, 403 if the pack is not set as shared, 404 if the pack does not exist -## `GET /api/v1/pleroma/accounts/:id/scrobbles` -### Requests a list of current and recent Listen activities for an account -* Method `GET` -* Authentication: not required -* Params: None -* Response: An array of media metadata entities. -* Example response: -```json -[ - { - "account": {...}, - "id": "1234", - "title": "Some Title", - "artist": "Some Artist", - "album": "Some Album", - "length": 180000, - "created_at": "2019-09-28T12:40:45.000Z" - } -] -``` - -## `POST /api/v1/pleroma/scrobble` -### Creates a new Listen activity for an account -* Method `POST` -* Authentication: required -* Params: - * `title`: the title of the media playing - * `album`: the album of the media playing [optional] - * `artist`: the artist of the media playing [optional] - * `length`: the length of the media playing [optional] -* Response: the newly created media metadata entity representing the Listen activity - # Emoji Reactions Emoji reactions work a lot like favourites do. They make it possible to react to a post with a single emoji character. To detect the presence of this feature, you can check `pleroma_emoji_reactions` entry in the features list of nodeinfo. diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -323,26 +323,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do :ok end - @spec listen(map()) :: {:ok, Activity.t()} | {:error, any()} - def listen(%{to: to, actor: actor, context: context, object: object} = params) do - additional = params[:additional] || %{} - # only accept false as false value - local = !(params[:local] == false) - published = params[:published] - - listen_data = - make_listen_data( - %{to: to, actor: actor, published: published, context: context, object: object}, - additional - ) - - with {:ok, activity} <- insert(listen_data, local), - _ <- notify_and_stream(activity), - :ok <- maybe_federate(activity) do - {:ok, activity} - end - end - @spec unfollow(User.t(), User.t(), String.t() | nil, boolean()) :: {:ok, Activity.t()} | nil | {:error, any()} def unfollow(follower, followed, activity_id \\ nil, local \\ true) do diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -384,37 +384,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def handle_incoming(%{"id" => id}, _options) when is_binary(id) and byte_size(id) < 8, do: :error - def handle_incoming( - %{"type" => "Listen", "object" => %{"type" => "Audio"} = object} = data, - options - ) do - actor = Containment.get_actor(data) - - data = - Map.put(data, "actor", actor) - |> fix_addressing - - with {:ok, %User{} = user} <- User.get_or_fetch_by_ap_id(data["actor"]) do - reply_depth = (options[:depth] || 0) + 1 - options = Keyword.put(options, :depth, reply_depth) - object = fix_object(object, options) - - params = %{ - to: data["to"], - object: object, - actor: user, - context: nil, - local: false, - published: data["published"], - additional: Map.take(data, ["cc", "id"]) - } - - ActivityPub.listen(params) - else - _e -> :error - end - end - @misskey_reactions %{ "like" => "👍", "love" => "❤️", @@ -712,8 +681,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do # internal -> Mastodon # """ - def prepare_outgoing(%{"type" => activity_type, "object" => object_id} = data) - when activity_type in ["Create", "Listen"] do + def prepare_outgoing(%{"type" => "Create", "object" => object_id} = data) do object = object_id |> Object.normalize(fetch: false) diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex @@ -703,21 +703,6 @@ defmodule Pleroma.Web.ActivityPub.Utils do |> Map.merge(additional) end - #### Listen-related helpers - def make_listen_data(params, additional) do - published = params.published || make_date() - - %{ - "type" => "Listen", - "to" => params.to |> Enum.uniq(), - "actor" => params.actor.ap_id, - "object" => params.object, - "published" => published, - "context" => params.context - } - |> Map.merge(additional) - end - #### Flag-related helpers @spec make_flag_data(map(), map()) :: map() def make_flag_data(%{actor: actor, context: context, content: content} = params, additional) do diff --git a/lib/pleroma/web/activity_pub/views/object_view.ex b/lib/pleroma/web/activity_pub/views/object_view.ex @@ -15,8 +15,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectView do Map.merge(base, additional) end - def render("object.json", %{object: %Activity{data: %{"type" => activity_type}} = activity}) - when activity_type in ["Create", "Listen"] do + def render("object.json", %{object: %Activity{data: %{"type" => "Create"}} = activity}) do base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header() object = Object.normalize(activity, fetch: false) diff --git a/lib/pleroma/web/api_spec.ex b/lib/pleroma/web/api_spec.ex @@ -79,7 +79,7 @@ defmodule Pleroma.Web.ApiSpec do "x-tagGroups": [ %{ "name" => "Accounts", - "tags" => ["Account actions", "Retrieve account information", "Scrobbles"] + "tags" => ["Account actions", "Retrieve account information"] }, %{ "name" => "Administration", diff --git a/lib/pleroma/web/api_spec/operations/pleroma_scrobble_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_scrobble_operation.ex @@ -1,102 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ApiSpec.PleromaScrobbleOperation do - alias OpenApiSpex.Operation - alias OpenApiSpex.Reference - alias OpenApiSpex.Schema - alias Pleroma.Web.ApiSpec.Schemas.Account - alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope - - import Pleroma.Web.ApiSpec.Helpers - - def open_api_operation(action) do - operation = String.to_existing_atom("#{action}_operation") - apply(__MODULE__, operation, []) - end - - def create_operation do - %Operation{ - tags: ["Scrobbles"], - summary: "Creates a new Listen activity for an account", - security: [%{"oAuth" => ["write"]}], - operationId: "PleromaAPI.ScrobbleController.create", - requestBody: request_body("Parameters", create_request(), requried: true), - responses: %{ - 200 => Operation.response("Scrobble", "application/json", scrobble()) - } - } - end - - def index_operation do - %Operation{ - tags: ["Scrobbles"], - summary: "Requests a list of current and recent Listen activities for an account", - operationId: "PleromaAPI.ScrobbleController.index", - parameters: [ - %Reference{"$ref": "#/components/parameters/accountIdOrNickname"} | pagination_params() - ], - security: [%{"oAuth" => ["read"]}], - responses: %{ - 200 => - Operation.response("Array of Scrobble", "application/json", %Schema{ - type: :array, - items: scrobble() - }) - } - } - end - - defp create_request do - %Schema{ - type: :object, - required: [:title], - properties: %{ - title: %Schema{type: :string, description: "The title of the media playing"}, - album: %Schema{type: :string, description: "The album of the media playing"}, - artist: %Schema{type: :string, description: "The artist of the media playing"}, - length: %Schema{type: :integer, description: "The length of the media playing"}, - visibility: %Schema{ - allOf: [VisibilityScope], - default: "public", - description: "Scrobble visibility" - } - }, - example: %{ - "title" => "Some Title", - "artist" => "Some Artist", - "album" => "Some Album", - "length" => 180_000 - } - } - end - - defp scrobble do - %Schema{ - type: :object, - properties: %{ - id: %Schema{type: :string}, - account: Account, - title: %Schema{type: :string, description: "The title of the media playing"}, - album: %Schema{type: :string, description: "The album of the media playing"}, - artist: %Schema{type: :string, description: "The artist of the media playing"}, - length: %Schema{ - type: :integer, - description: "The length of the media playing", - nullable: true - }, - created_at: %Schema{type: :string, format: :"date-time"} - }, - example: %{ - "id" => "1234", - "account" => Account.schema().example, - "title" => "Some Title", - "artist" => "Some Artist", - "album" => "Some Album", - "length" => 180_000, - "created_at" => "2019-09-28T12:40:45.000Z" - } - } - end -end diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex @@ -406,12 +406,6 @@ defmodule Pleroma.Web.CommonAPI do |> check_expiry_date() end - def listen(user, data) do - with {:ok, draft} <- ActivityDraft.listen(user, data) do - ActivityPub.listen(draft.changes) - end - end - def post(user, %{status: _} = data) do with {:ok, draft} <- ActivityDraft.create(user, data) do ActivityPub.create(draft.changes, draft.preview?) diff --git a/lib/pleroma/web/common_api/activity_draft.ex b/lib/pleroma/web/common_api/activity_draft.ex @@ -64,30 +64,6 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do |> validate() end - def listen(user, params) do - user - |> new(params) - |> visibility() - |> to_and_cc() - |> context() - |> listen_object() - |> with_valid(&changes/1) - |> validate() - end - - defp listen_object(draft) do - object = - draft.params - |> Map.take([:album, :artist, :title, :length]) - |> Map.new(fn {key, value} -> {to_string(key), value} end) - |> Map.put("type", "Audio") - |> Map.put("to", draft.to) - |> Map.put("cc", draft.cc) - |> Map.put("actor", draft.user.ap_id) - - %__MODULE__{draft | object: object} - end - defp put_params(draft, params) do params = Map.put_new(params, :in_reply_to_status_id, params[:in_reply_to_id]) %__MODULE__{draft | params: params} diff --git a/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex b/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex @@ -1,52 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.PleromaAPI.ScrobbleController do - use Pleroma.Web, :controller - - import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2] - - alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.Plugs.OAuthScopesPlug - - plug(Pleroma.Web.ApiSpec.CastAndValidate) - - plug( - OAuthScopesPlug, - %{scopes: ["read"], fallback: :proceed_unauthenticated} when action == :index - ) - - plug(OAuthScopesPlug, %{scopes: ["write"]} when action == :create) - - defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaScrobbleOperation - - def create(%{assigns: %{user: user}, body_params: params} = conn, _) do - with {:ok, activity} <- CommonAPI.listen(user, params) do - render(conn, "show.json", activity: activity, for: user) - else - {:error, message} -> - conn - |> put_status(:bad_request) - |> json(%{"error" => message}) - end - end - - def index(%{assigns: %{user: reading_user}} = conn, %{id: id} = params) do - with %User{} = user <- User.get_cached_by_nickname_or_id(id, for: reading_user) do - params = Map.put(params, :type, ["Listen"]) - - activities = ActivityPub.fetch_user_abstract_activities(user, reading_user, params) - - conn - |> add_link_headers(activities) - |> render("index.json", %{ - activities: activities, - for: reading_user, - as: :activity - }) - end - end -end diff --git a/lib/pleroma/web/pleroma_api/views/scrobble_view.ex b/lib/pleroma/web/pleroma_api/views/scrobble_view.ex @@ -1,37 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.PleromaAPI.ScrobbleView do - use Pleroma.Web, :view - - require Pleroma.Constants - - alias Pleroma.Activity - alias Pleroma.HTML - alias Pleroma.Object - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.CommonAPI.Utils - alias Pleroma.Web.MastodonAPI.AccountView - - def render("show.json", %{activity: %Activity{data: %{"type" => "Listen"}} = activity} = opts) do - object = Object.normalize(activity, fetch: false) - - user = CommonAPI.get_user(activity.data["actor"]) - created_at = Utils.to_masto_date(activity.data["published"]) - - %{ - id: activity.id, - account: AccountView.render("show.json", %{user: user, for: opts[:for]}), - created_at: created_at, - title: object.data["title"] |> HTML.strip_tags(), - artist: object.data["artist"] |> HTML.strip_tags(), - album: object.data["album"] |> HTML.strip_tags(), - length: object.data["length"] - } - end - - def render("index.json", opts) do - safe_render_many(opts.activities, __MODULE__, "show.json", opts) - end -end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex @@ -568,8 +568,6 @@ defmodule Pleroma.Web.Router do get("/mascot", MascotController, :show) put("/mascot", MascotController, :update) - post("/scrobble", ScrobbleController, :create) - get("/backups", BackupController, :index) post("/backups", BackupController, :create) end @@ -601,7 +599,6 @@ defmodule Pleroma.Web.Router do scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do pipe_through(:api) - get("/accounts/:id/scrobbles", ScrobbleController, :index) get("/federation_status", InstancesController, :show) end diff --git a/test/pleroma/web/activity_pub/activity_pub_test.exs b/test/pleroma/web/activity_pub/activity_pub_test.exs @@ -597,42 +597,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do end end - describe "listen activities" do - test "does not increase user note count" do - user = insert(:user) - - {:ok, activity} = - ActivityPub.listen(%{ - to: ["https://www.w3.org/ns/activitystreams#Public"], - actor: user, - context: "", - object: %{ - "actor" => user.ap_id, - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "artist" => "lain", - "title" => "lain radio episode 1", - "length" => 180_000, - "type" => "Audio" - } - }) - - assert activity.actor == user.ap_id - - user = User.get_cached_by_id(user.id) - assert user.note_count == 0 - end - - test "can be fetched into a timeline" do - _listen_activity_1 = insert(:listen) - _listen_activity_2 = insert(:listen) - _listen_activity_3 = insert(:listen) - - timeline = ActivityPub.fetch_activities([], %{type: ["Listen"]}) - - assert length(timeline) == 3 - end - end - describe "create activities" do setup do [user: insert(:user)] diff --git a/test/pleroma/web/activity_pub/transmogrifier/audio_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/audio_handling_test.exs @@ -10,41 +10,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.AudioHandlingTest do alias Pleroma.Object alias Pleroma.Web.ActivityPub.Transmogrifier - import Pleroma.Factory - - test "it works for incoming listens" do - _user = insert(:user, ap_id: "http://mastodon.example.org/users/admin") - - data = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "type" => "Listen", - "id" => "http://mastodon.example.org/users/admin/listens/1234/activity", - "actor" => "http://mastodon.example.org/users/admin", - "object" => %{ - "type" => "Audio", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "id" => "http://mastodon.example.org/users/admin/listens/1234", - "attributedTo" => "http://mastodon.example.org/users/admin", - "title" => "lain radio episode 1", - "artist" => "lain", - "album" => "lain radio", - "length" => 180_000 - } - } - - {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - - object = Object.normalize(activity, fetch: false) - - assert object.data["title"] == "lain radio episode 1" - assert object.data["artist"] == "lain" - assert object.data["album"] == "lain radio" - assert object.data["length"] == 180_000 - end - test "Funkwhale Audio object" do Tesla.Mock.mock(fn %{url: "https://channels.tests.funkwhale.audio/federation/actors/compositions"} -> diff --git a/test/pleroma/web/activity_pub/transmogrifier_test.exs b/test/pleroma/web/activity_pub/transmogrifier_test.exs @@ -301,20 +301,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do assert is_nil(modified["bcc"]) end - test "it can handle Listen activities" do - listen_activity = insert(:listen) - - {:ok, modified} = Transmogrifier.prepare_outgoing(listen_activity.data) - - assert modified["type"] == "Listen" - - user = insert(:user) - - {:ok, activity} = CommonAPI.listen(user, %{"title" => "lain radio episode 1"}) - - {:ok, _modified} = Transmogrifier.prepare_outgoing(activity.data) - end - test "custom emoji urls are URI encoded" do # :dinosaur: filename has a space -> dino walking.gif user = insert(:user) diff --git a/test/pleroma/web/common_api_test.exs b/test/pleroma/web/common_api_test.exs @@ -1429,45 +1429,6 @@ defmodule Pleroma.Web.CommonAPITest do end end - describe "listen/2" do - test "returns a valid activity" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.listen(user, %{ - title: "lain radio episode 1", - album: "lain radio", - artist: "lain", - length: 180_000 - }) - - object = Object.normalize(activity, fetch: false) - - assert object.data["title"] == "lain radio episode 1" - - assert Visibility.get_visibility(activity) == "public" - end - - test "respects visibility=private" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.listen(user, %{ - title: "lain radio episode 1", - album: "lain radio", - artist: "lain", - length: 180_000, - visibility: "private" - }) - - object = Object.normalize(activity, fetch: false) - - assert object.data["title"] == "lain radio episode 1" - - assert Visibility.get_visibility(activity) == "private" - end - end - describe "get_user/1" do test "gets user by ap_id" do user = insert(:user) diff --git a/test/pleroma/web/pleroma_api/controllers/scrobble_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/scrobble_controller_test.exs @@ -1,60 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.PleromaAPI.ScrobbleControllerTest do - use Pleroma.Web.ConnCase, async: true - - alias Pleroma.Web.CommonAPI - - describe "POST /api/v1/pleroma/scrobble" do - test "works correctly" do - %{conn: conn} = oauth_access(["write"]) - - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/pleroma/scrobble", %{ - "title" => "lain radio episode 1", - "artist" => "lain", - "album" => "lain radio", - "length" => "180000" - }) - - assert %{"title" => "lain radio episode 1"} = json_response_and_validate_schema(conn, 200) - end - end - - describe "GET /api/v1/pleroma/accounts/:id/scrobbles" do - test "works correctly" do - %{user: user, conn: conn} = oauth_access(["read"]) - - {:ok, _activity} = - CommonAPI.listen(user, %{ - title: "lain radio episode 1", - artist: "lain", - album: "lain radio" - }) - - {:ok, _activity} = - CommonAPI.listen(user, %{ - title: "lain radio episode 2", - artist: "lain", - album: "lain radio" - }) - - {:ok, _activity} = - CommonAPI.listen(user, %{ - title: "lain radio episode 3", - artist: "lain", - album: "lain radio" - }) - - conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/scrobbles") - - result = json_response_and_validate_schema(conn, 200) - - assert length(result) == 3 - end - end -end diff --git a/test/pleroma/web/pleroma_api/views/scrobble_view_test.exs b/test/pleroma/web/pleroma_api/views/scrobble_view_test.exs @@ -1,20 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.PleromaAPI.ScrobbleViewTest do - use Pleroma.DataCase, async: true - - alias Pleroma.Web.PleromaAPI.ScrobbleView - - import Pleroma.Factory - - test "successfully renders a Listen activity (pleroma extension)" do - listen_activity = insert(:listen) - - status = ScrobbleView.render("show.json", activity: listen_activity) - - assert status.length == listen_activity.data["object"]["length"] - assert status.title == listen_activity.data["object"]["title"] - end -end diff --git a/test/support/factory.ex b/test/support/factory.ex @@ -193,25 +193,6 @@ defmodule Pleroma.Factory do } end - def listen_factory do - audio = insert(:audio) - - data = %{ - "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(), - "type" => "Listen", - "actor" => audio.data["actor"], - "to" => audio.data["to"], - "object" => audio.data, - "published" => audio.data["published"] - } - - %Pleroma.Activity{ - data: data, - actor: data["actor"], - recipients: data["to"] - } - end - def direct_note_factory do user2 = insert(:user)