Merge branch 'feature/scrobbling' into 'develop'
media scrobbling ("now playing" API) See merge request pleroma/pleroma!1732
This commit is contained in:
commit
ed176e7402
@ -7,6 +7,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
### Added
|
### Added
|
||||||
- Refreshing poll results for remote polls
|
- Refreshing poll results for remote polls
|
||||||
- Admin API: Add ability to require password reset
|
- Admin API: Add ability to require password reset
|
||||||
|
- Mastodon API: Account entities now include `follow_requests_count` (planned Mastodon 3.x addition)
|
||||||
|
- Pleroma API: `GET /api/v1/pleroma/accounts/:id/scrobbles` to get a list of recently scrobbled items
|
||||||
|
- Pleroma API: `POST /api/v1/pleroma/scrobble` to scrobble a media item
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- **Breaking:** Elixir >=1.8 is now required (was >= 1.7)
|
- **Breaking:** Elixir >=1.8 is now required (was >= 1.7)
|
||||||
|
@ -439,3 +439,35 @@ The status posting endpoint takes an additional parameter, `in_reply_to_conversa
|
|||||||
* Params: None
|
* Params: None
|
||||||
* Response: the archive of the pack with a 200 status code, 403 if the pack is not set as shared,
|
* 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
|
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
|
||||||
|
@ -248,6 +248,26 @@ def create(%{to: to, actor: actor, context: context, object: object} = params, f
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
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]
|
||||||
|
|
||||||
|
with listen_data <-
|
||||||
|
make_listen_data(
|
||||||
|
%{to: to, actor: actor, published: published, context: context, object: object},
|
||||||
|
additional
|
||||||
|
),
|
||||||
|
{:ok, activity} <- insert(listen_data, local),
|
||||||
|
:ok <- maybe_federate(activity) do
|
||||||
|
{:ok, activity}
|
||||||
|
else
|
||||||
|
{:error, message} ->
|
||||||
|
{:error, message}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def accept(%{to: to, actor: actor, object: object} = params) do
|
def accept(%{to: to, actor: actor, object: object} = params) do
|
||||||
# only accept false as false value
|
# only accept false as false value
|
||||||
local = !(params[:local] == false)
|
local = !(params[:local] == false)
|
||||||
@ -588,6 +608,23 @@ defp restrict_thread_visibility(query, %{"user" => %User{ap_id: ap_id}}, _) do
|
|||||||
|
|
||||||
defp restrict_thread_visibility(query, _, _), do: query
|
defp restrict_thread_visibility(query, _, _), do: query
|
||||||
|
|
||||||
|
def fetch_user_abstract_activities(user, reading_user, params \\ %{}) do
|
||||||
|
params =
|
||||||
|
params
|
||||||
|
|> Map.put("user", reading_user)
|
||||||
|
|> Map.put("actor_id", user.ap_id)
|
||||||
|
|> Map.put("whole_db", true)
|
||||||
|
|
||||||
|
recipients =
|
||||||
|
user_activities_recipients(%{
|
||||||
|
"godmode" => params["godmode"],
|
||||||
|
"reading_user" => reading_user
|
||||||
|
})
|
||||||
|
|
||||||
|
fetch_activities(recipients, params)
|
||||||
|
|> Enum.reverse()
|
||||||
|
end
|
||||||
|
|
||||||
def fetch_user_activities(user, reading_user, params \\ %{}) do
|
def fetch_user_activities(user, reading_user, params \\ %{}) do
|
||||||
params =
|
params =
|
||||||
params
|
params
|
||||||
|
@ -430,6 +430,36 @@ def handle_incoming(
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
options = Keyword.put(options, :depth, (options[:depth] || 0) + 1)
|
||||||
|
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
|
||||||
|
|
||||||
def handle_incoming(
|
def handle_incoming(
|
||||||
%{"type" => "Follow", "object" => followed, "actor" => follower, "id" => id} = data,
|
%{"type" => "Follow", "object" => followed, "actor" => follower, "id" => id} = data,
|
||||||
_options
|
_options
|
||||||
@ -765,7 +795,8 @@ def prepare_object(object) do
|
|||||||
# internal -> Mastodon
|
# internal -> Mastodon
|
||||||
# """
|
# """
|
||||||
|
|
||||||
def prepare_outgoing(%{"type" => "Create", "object" => object_id} = data) do
|
def prepare_outgoing(%{"type" => activity_type, "object" => object_id} = data)
|
||||||
|
when activity_type in ["Create", "Listen"] do
|
||||||
object =
|
object =
|
||||||
object_id
|
object_id
|
||||||
|> Object.normalize()
|
|> Object.normalize()
|
||||||
|
@ -20,7 +20,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||||||
require Logger
|
require Logger
|
||||||
require Pleroma.Constants
|
require Pleroma.Constants
|
||||||
|
|
||||||
@supported_object_types ["Article", "Note", "Video", "Page", "Question", "Answer"]
|
@supported_object_types ["Article", "Note", "Video", "Page", "Question", "Answer", "Audio"]
|
||||||
@supported_report_states ~w(open closed resolved)
|
@supported_report_states ~w(open closed resolved)
|
||||||
@valid_visibilities ~w(public unlisted private direct)
|
@valid_visibilities ~w(public unlisted private direct)
|
||||||
|
|
||||||
@ -581,6 +581,21 @@ def make_create_data(params, additional) do
|
|||||||
|> Map.merge(additional)
|
|> Map.merge(additional)
|
||||||
end
|
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
|
#### Flag-related helpers
|
||||||
@spec make_flag_data(map(), map()) :: map()
|
@spec make_flag_data(map(), map()) :: map()
|
||||||
def make_flag_data(%{actor: actor, context: context, content: content} = params, additional) do
|
def make_flag_data(%{actor: actor, context: context, content: content} = params, additional) do
|
||||||
|
@ -15,7 +15,8 @@ def render("object.json", %{object: %Object{} = object}) do
|
|||||||
Map.merge(base, additional)
|
Map.merge(base, additional)
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("object.json", %{object: %Activity{data: %{"type" => "Create"}} = activity}) do
|
def render("object.json", %{object: %Activity{data: %{"type" => activity_type}} = activity})
|
||||||
|
when activity_type in ["Create", "Listen"] do
|
||||||
base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header()
|
base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header()
|
||||||
object = Object.normalize(activity)
|
object = Object.normalize(activity)
|
||||||
|
|
||||||
|
@ -212,6 +212,27 @@ def check_expiry_date(expiry_str) do
|
|||||||
|> check_expiry_date()
|
|> check_expiry_date()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def listen(user, %{"title" => _} = data) do
|
||||||
|
with visibility <- data["visibility"] || "public",
|
||||||
|
{to, cc} <- get_to_and_cc(user, [], nil, visibility, nil),
|
||||||
|
listen_data <-
|
||||||
|
Map.take(data, ["album", "artist", "title", "length"])
|
||||||
|
|> Map.put("type", "Audio")
|
||||||
|
|> Map.put("to", to)
|
||||||
|
|> Map.put("cc", cc)
|
||||||
|
|> Map.put("actor", user.ap_id),
|
||||||
|
{:ok, activity} <-
|
||||||
|
ActivityPub.listen(%{
|
||||||
|
actor: user,
|
||||||
|
to: to,
|
||||||
|
object: listen_data,
|
||||||
|
context: Utils.generate_context_id(),
|
||||||
|
additional: %{"cc" => cc}
|
||||||
|
}) do
|
||||||
|
{:ok, activity}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def post(user, %{"status" => _} = data) do
|
def post(user, %{"status" => _} = data) do
|
||||||
with {:ok, draft} <- Pleroma.Web.CommonAPI.ActivityDraft.create(user, data) do
|
with {:ok, draft} <- Pleroma.Web.CommonAPI.ActivityDraft.create(user, data) do
|
||||||
draft.changes
|
draft.changes
|
||||||
|
@ -368,6 +368,27 @@ def render("attachment.json", %{attachment: attachment}) do
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def render("listen.json", %{activity: %Activity{data: %{"type" => "Listen"}} = activity} = opts) do
|
||||||
|
object = Object.normalize(activity)
|
||||||
|
|
||||||
|
user = get_user(activity.data["actor"])
|
||||||
|
created_at = Utils.to_masto_date(activity.data["published"])
|
||||||
|
|
||||||
|
%{
|
||||||
|
id: activity.id,
|
||||||
|
account: AccountView.render("account.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("listens.json", opts) do
|
||||||
|
safe_render_many(opts.activities, StatusView, "listen.json", opts)
|
||||||
|
end
|
||||||
|
|
||||||
def render("poll.json", %{object: object} = opts) do
|
def render("poll.json", %{object: object} = opts) do
|
||||||
{multiple, options} =
|
{multiple, options} =
|
||||||
case object.data do
|
case object.data do
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 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, fetch_integer_param: 2]
|
||||||
|
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
alias Pleroma.Web.MastodonAPI.StatusView
|
||||||
|
|
||||||
|
def new_scrobble(%{assigns: %{user: user}} = conn, %{"title" => _} = params) do
|
||||||
|
params =
|
||||||
|
if !params["length"] do
|
||||||
|
params
|
||||||
|
else
|
||||||
|
params
|
||||||
|
|> Map.put("length", fetch_integer_param(params, "length"))
|
||||||
|
end
|
||||||
|
|
||||||
|
with {:ok, activity} <- CommonAPI.listen(user, params) do
|
||||||
|
conn
|
||||||
|
|> put_view(StatusView)
|
||||||
|
|> render("listen.json", %{activity: activity, for: user})
|
||||||
|
else
|
||||||
|
{:error, message} ->
|
||||||
|
conn
|
||||||
|
|> put_status(:bad_request)
|
||||||
|
|> json(%{"error" => message})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def user_scrobbles(%{assigns: %{user: reading_user}} = conn, params) do
|
||||||
|
with %User{} = user <- User.get_cached_by_nickname_or_id(params["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)
|
||||||
|
|> put_view(StatusView)
|
||||||
|
|> render("listens.json", %{
|
||||||
|
activities: activities,
|
||||||
|
for: reading_user,
|
||||||
|
as: :activity
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -300,6 +300,17 @@ defmodule Pleroma.Web.Router do
|
|||||||
patch("/conversations/:id", PleromaAPIController, :update_conversation)
|
patch("/conversations/:id", PleromaAPIController, :update_conversation)
|
||||||
post("/notifications/read", PleromaAPIController, :read_notification)
|
post("/notifications/read", PleromaAPIController, :read_notification)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
scope [] do
|
||||||
|
pipe_through(:oauth_write)
|
||||||
|
post("/scrobble", ScrobbleController, :new_scrobble)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
|
||||||
|
pipe_through([:api, :oauth_read_or_public])
|
||||||
|
|
||||||
|
get("/accounts/:id/scrobbles", ScrobbleController, :user_scrobbles)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/v1", Pleroma.Web.MastodonAPI do
|
scope "/api/v1", Pleroma.Web.MastodonAPI do
|
||||||
|
@ -71,6 +71,47 @@ def note_factory(attrs \\ %{}) do
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def audio_factory(attrs \\ %{}) do
|
||||||
|
text = sequence(:text, &"lain radio episode #{&1}")
|
||||||
|
|
||||||
|
user = attrs[:user] || insert(:user)
|
||||||
|
|
||||||
|
data = %{
|
||||||
|
"type" => "Audio",
|
||||||
|
"id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(),
|
||||||
|
"artist" => "lain",
|
||||||
|
"title" => text,
|
||||||
|
"album" => "lain radio",
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"published" => DateTime.utc_now() |> DateTime.to_iso8601(),
|
||||||
|
"actor" => user.ap_id,
|
||||||
|
"length" => 180_000
|
||||||
|
}
|
||||||
|
|
||||||
|
%Pleroma.Object{
|
||||||
|
data: merge_attributes(data, Map.get(attrs, :data, %{}))
|
||||||
|
}
|
||||||
|
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
|
def direct_note_factory do
|
||||||
user2 = insert(:user)
|
user2 = insert(:user)
|
||||||
|
|
||||||
|
@ -257,6 +257,42 @@ test "adds an id to a given object if it lacks one and is a note and inserts it
|
|||||||
end
|
end
|
||||||
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.info.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
|
describe "create activities" do
|
||||||
test "removes doubled 'to' recipients" do
|
test "removes doubled 'to' recipients" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
@ -177,6 +177,35 @@ test "it works for incoming questions" do
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it works for incoming listens" do
|
||||||
|
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",
|
||||||
|
"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)
|
||||||
|
|
||||||
|
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 "it rewrites Note votes to Answers and increments vote counters on question activities" do
|
test "it rewrites Note votes to Answers and increments vote counters on question activities" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
@ -1190,6 +1219,20 @@ test "it strips BCC field" do
|
|||||||
|
|
||||||
assert is_nil(modified["bcc"])
|
assert is_nil(modified["bcc"])
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "user upgrade" do
|
describe "user upgrade" do
|
||||||
|
@ -510,4 +510,43 @@ test "does not allow to vote twice" do
|
|||||||
assert {:error, "Already voted"} == CommonAPI.vote(other_user, object, [1])
|
assert {:error, "Already voted"} == CommonAPI.vote(other_user, object, [1])
|
||||||
end
|
end
|
||||||
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)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
assert object.data["title"] == "lain radio episode 1"
|
||||||
|
|
||||||
|
assert Visibility.get_visibility(activity) == "private"
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -608,4 +608,13 @@ test "visibility/list" do
|
|||||||
|
|
||||||
assert status.visibility == "list"
|
assert status.visibility == "list"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "successfully renders a Listen activity (pleroma extension)" do
|
||||||
|
listen_activity = insert(:listen)
|
||||||
|
|
||||||
|
status = StatusView.render("listen.json", activity: listen_activity)
|
||||||
|
|
||||||
|
assert status.length == listen_activity.data["object"]["length"]
|
||||||
|
assert status.title == listen_activity.data["object"]["title"]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -0,0 +1,63 @@
|
|||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.PleromaAPI.ScrobbleControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
describe "POST /api/v1/pleroma/scrobble" do
|
||||||
|
test "works correctly", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> 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(conn, 200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET /api/v1/pleroma/accounts/:id/scrobbles" do
|
||||||
|
test "works correctly", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{: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 =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/pleroma/accounts/#{user.id}/scrobbles")
|
||||||
|
|
||||||
|
result = json_response(conn, 200)
|
||||||
|
|
||||||
|
assert length(result) == 3
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user