Podcast episodes play; servers with NodeInfo2 only are described
Castopod announces an episode with a Note that only links to the episode's page, which serves a PodcastEpisode. PrivaPub reads PodcastEpisode (its words in description, its sound in audio, its cover in image), and a Note that is only a link to its author's own episode takes the episode's sound, title, cover and words, so the post plays. A server that publishes no NodeInfo is described from NodeInfo2 (/.well-known/x-nodeinfo2), as Castopod publishes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
1 parent
3df3c333bf
commit
69ab8e50eb
7 files changed
+163
-3
No files matched your search
@@ -212,6 +212,47 @@ namespace PrivaPub.Tests.Federation
|
||||
Assert.False(await DB.Default.Find<PrivaPub.Models.Federation.Follower>().Match(f => f.LocalActorId == alice.Id && f.ActorURI == forte.Id).ExecuteAnyAsync(token));
|
||||
}
|
||||
|
||||
// Castopod announces an episode with a note that only links to it: the post takes the episode's sound, title and cover
|
||||
[Fact]
|
||||
public async Task A_note_that_only_links_its_authors_episode_carries_the_episodes_sound()
|
||||
{
|
||||
var token = TestContext.Current.CancellationToken;
|
||||
var (_, alice) = await _harness.Persona("alice");
|
||||
var podcast = new RemoteActor(_harness.Peer, "pod");
|
||||
await DB.Default.SaveAsync(new PrivaPub.Models.Social.Following
|
||||
{
|
||||
AvatarId = alice.Id, TargetActorURI = podcast.Id, TargetInboxURL = podcast.Id + "/inbox", State = PrivaPub.Models.Social.FollowState.Accepted
|
||||
}, token);
|
||||
var origin = new Uri(podcast.Id).GetLeftPart(UriPartial.Authority);
|
||||
var episodePath = $"/episodes/{Guid.NewGuid():N}";
|
||||
_harness.Peer.Serve(episodePath, new JsonObject
|
||||
{
|
||||
["id"] = origin + episodePath, ["type"] = "PodcastEpisode", ["attributedTo"] = podcast.Id, ["to"] = new JsonArray("https://www.w3.org/ns/activitystreams#Public"),
|
||||
["description"] = new JsonObject { ["type"] = "Note", ["content"] = "the meadow", ["mediaType"] = "text/markdown" },
|
||||
["image"] = new JsonObject { ["type"] = "Image", ["url"] = origin + "/cover.png" },
|
||||
["audio"] = new JsonObject
|
||||
{
|
||||
["id"] = origin + "/one.mp3", ["type"] = "Audio", ["name"] = "Episode one", ["duration"] = 4.0,
|
||||
["url"] = new JsonObject { ["href"] = origin + "/one.mp3", ["type"] = "Link", ["mediaType"] = "audio/mpeg" }
|
||||
}
|
||||
}.ToJsonString());
|
||||
var noteId = NewId(podcast, "posts");
|
||||
|
||||
await _harness.Deliver(podcast, "/human-centipede", new JsonObject
|
||||
{
|
||||
["id"] = noteId + "/activity", ["type"] = "Create", ["actor"] = podcast.Id, ["to"] = new JsonArray("https://www.w3.org/ns/activitystreams#Public"),
|
||||
["object"] = new JsonObject
|
||||
{
|
||||
["id"] = noteId, ["type"] = "Note", ["attributedTo"] = podcast.Id, ["to"] = new JsonArray("https://www.w3.org/ns/activitystreams#Public"),
|
||||
["content"] = $"<a href=\"{origin + episodePath}\">Episode one</a><br/>", ["published"] = DateTime.UtcNow.ToString("O")
|
||||
}
|
||||
});
|
||||
|
||||
var post = await DB.Default.Find<Post>().Match(p => p.ObjectURI == noteId).ExecuteSingleAsync(token);
|
||||
Assert.Equal(origin + "/one.mp3", Assert.Single(post.Media).RemoteURL);
|
||||
Assert.Equal(("Episode one", origin + "/cover.png", 4.0), (post.Title, post.CoverURL, post.Audio.DurationSeconds));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A_join_of_a_local_post_is_answered_with_ignore()
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user