devin ivy 80ada8f476
Video lexicons and appview views (#2751)
* lexicon: initial lexicons for video embeds in bsky app

* lexicon: fix video caption file size limit

* codegen

* appview: stub out video embed view logic

* api prerelease

* api prerelease

* lexicon: video upload/processing lexicons

* tidy

* lexicon: app.bsky.video lexicons for uploads

* codegen

* api prerelease

* appview: present video embeds on posts

* appview: snaps

* changeset

* appview: fix wiring of video url config
2024-08-28 19:03:35 -04:00

193 lines
5.7 KiB
TypeScript

import { AppBskyFeedPost, AtpAgent } from '@atproto/api'
import { TestNetwork, SeedClient, basicSeed } from '@atproto/dev-env'
import { forSnapshot, stripViewerFromPost } from '../_util'
import { RecordEmbed, VideoEmbed } from '../../src/views/types'
import { RecordWithMedia } from '../../dist/views/types'
import { ids } from '../../src/lexicon/lexicons'
describe('pds posts views', () => {
let network: TestNetwork
let agent: AtpAgent
let pdsAgent: AtpAgent
let sc: SeedClient
beforeAll(async () => {
network = await TestNetwork.create({
dbPostgresSchema: 'bsky_views_posts',
})
agent = network.bsky.getClient()
pdsAgent = network.pds.getClient()
sc = network.getSeedClient()
await basicSeed(sc)
await network.processAll()
})
afterAll(async () => {
await network.close()
})
it('fetches posts', async () => {
const uris = [
sc.posts[sc.dids.alice][0].ref.uriStr,
sc.posts[sc.dids.alice][1].ref.uriStr,
sc.posts[sc.dids.bob][0].ref.uriStr,
sc.posts[sc.dids.carol][0].ref.uriStr,
sc.posts[sc.dids.dan][1].ref.uriStr,
sc.replies[sc.dids.alice][0].ref.uriStr,
]
const posts = await agent.api.app.bsky.feed.getPosts(
{ uris },
{
headers: await network.serviceHeaders(
sc.dids.alice,
ids.AppBskyFeedGetPosts,
),
},
)
expect(posts.data.posts.length).toBe(uris.length)
expect(forSnapshot(posts.data.posts)).toMatchSnapshot()
})
it('fetches posts unauthed', async () => {
const uris = [
sc.posts[sc.dids.alice][0].ref.uriStr,
sc.posts[sc.dids.alice][1].ref.uriStr,
sc.posts[sc.dids.bob][0].ref.uriStr,
sc.posts[sc.dids.carol][0].ref.uriStr,
sc.posts[sc.dids.dan][1].ref.uriStr,
sc.replies[sc.dids.alice][0].ref.uriStr,
]
const authed = await agent.api.app.bsky.feed.getPosts(
{ uris },
{
headers: await network.serviceHeaders(
sc.dids.alice,
ids.AppBskyFeedGetPosts,
),
},
)
const unauthed = await agent.api.app.bsky.feed.getPosts({
uris,
})
const stripped = authed.data.posts.map((p) => stripViewerFromPost(p))
expect(unauthed.data.posts).toEqual(stripped)
})
it('handles repeat uris', async () => {
const uris = [
sc.posts[sc.dids.alice][0].ref.uriStr,
sc.posts[sc.dids.alice][0].ref.uriStr,
sc.posts[sc.dids.bob][0].ref.uriStr,
sc.posts[sc.dids.alice][0].ref.uriStr,
sc.posts[sc.dids.bob][0].ref.uriStr,
]
const posts = await agent.api.app.bsky.feed.getPosts({ uris })
expect(posts.data.posts.length).toBe(2)
const receivedUris = posts.data.posts.map((p) => p.uri).sort()
const expected = [
sc.posts[sc.dids.alice][0].ref.uriStr,
sc.posts[sc.dids.bob][0].ref.uriStr,
].sort()
expect(receivedUris).toEqual(expected)
})
it('allows for creating posts with tags', async () => {
const post: AppBskyFeedPost.Record = {
text: 'hello world',
tags: ['javascript', 'hehe'],
createdAt: new Date().toISOString(),
}
const { uri } = await pdsAgent.api.app.bsky.feed.post.create(
{ repo: sc.dids.alice },
post,
sc.getHeaders(sc.dids.alice),
)
await network.processAll()
const { data } = await agent.api.app.bsky.feed.getPosts({ uris: [uri] })
expect(data.posts.length).toBe(1)
// @ts-ignore we know it's a post record
expect(data.posts[0].record.tags).toEqual(['javascript', 'hehe'])
})
it('embeds video.', async () => {
const { data: video } = await pdsAgent.api.com.atproto.repo.uploadBlob(
Buffer.from('notarealvideo'),
{
headers: sc.getHeaders(sc.dids.alice),
encoding: 'image/mp4',
},
)
const { uri } = await pdsAgent.api.app.bsky.feed.post.create(
{ repo: sc.dids.alice },
{
text: 'video',
createdAt: new Date().toISOString(),
embed: {
$type: 'app.bsky.embed.video',
video: video.blob,
alt: 'alt text',
aspectRatio: { height: 3, width: 4 },
} satisfies VideoEmbed,
},
sc.getHeaders(sc.dids.alice),
)
await network.processAll()
const { data } = await agent.app.bsky.feed.getPosts({ uris: [uri] })
expect(data.posts.length).toBe(1)
expect(forSnapshot(data.posts[0])).toMatchSnapshot()
})
it('embeds video with record.', async () => {
const { data: video } = await pdsAgent.api.com.atproto.repo.uploadBlob(
Buffer.from('notarealvideo'),
{
headers: sc.getHeaders(sc.dids.alice),
encoding: 'image/mp4',
},
)
const embedRecord = await pdsAgent.api.app.bsky.feed.post.create(
{ repo: sc.dids.alice },
{
text: 'embedded',
createdAt: new Date().toISOString(),
},
sc.getHeaders(sc.dids.alice),
)
const { uri } = await pdsAgent.api.app.bsky.feed.post.create(
{ repo: sc.dids.alice },
{
text: 'video',
createdAt: new Date().toISOString(),
embed: {
$type: 'app.bsky.embed.recordWithMedia',
record: {
record: {
uri: embedRecord.uri,
cid: embedRecord.cid,
},
} satisfies RecordEmbed,
media: {
$type: 'app.bsky.embed.video',
video: video.blob,
alt: 'alt text',
aspectRatio: { height: 3, width: 4 },
} satisfies VideoEmbed,
} satisfies RecordWithMedia,
},
sc.getHeaders(sc.dids.alice),
)
await network.processAll()
const { data } = await agent.app.bsky.feed.getPosts({ uris: [uri] })
expect(data.posts.length).toBe(1)
expect(forSnapshot(data.posts[0])).toMatchSnapshot()
})
})