Skip to content

Commit

Permalink
Paralellize image uploads (#5535)
Browse files Browse the repository at this point in the history
* Remove unsafe type coercions from posting embed

* Extract resolveEmbed into a separate function

* Refactor to if-else because these are mutually exclusive

* Refactor resolveEmbed to early returns

* Separate resolving embed and media

* Parallelize image upload

* Prioritize not dropping media
  • Loading branch information
gaearon authored Oct 1, 2024
1 parent b59d6df commit a7ee561
Showing 1 changed file with 117 additions and 137 deletions.
254 changes: 117 additions & 137 deletions src/lib/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
RichText,
} from '@atproto/api'

import {isNetworkError} from '#/lib/strings/errors'
import {shortenLinks, stripInvalidMentions} from '#/lib/strings/rich-text-manip'
import {logger} from '#/logger'
import {ComposerImage, compressImage} from '#/state/gallery'
import {writePostgateRecord} from '#/state/queries/postgate'
Expand All @@ -22,8 +24,6 @@ import {
threadgateAllowUISettingToAllowRecordValue,
writeThreadgateRecord,
} from '#/state/queries/threadgate'
import {isNetworkError} from 'lib/strings/errors'
import {shortenLinks, stripInvalidMentions} from 'lib/strings/rich-text-manip'
import {LinkMeta} from '../link-meta/link-meta'
import {uploadBlob} from './upload-blob'

Expand Down Expand Up @@ -60,13 +60,6 @@ interface PostOpts {
}

export async function post(agent: BskyAgent, opts: PostOpts) {
let embed:
| AppBskyEmbedImages.Main
| AppBskyEmbedExternal.Main
| AppBskyEmbedRecord.Main
| AppBskyEmbedVideo.Main
| AppBskyEmbedRecordWithMedia.Main
| undefined
let reply
let rt = new RichText({text: opts.rawText.trimEnd()}, {cleanNewlines: true})

Expand All @@ -77,134 +70,7 @@ export async function post(agent: BskyAgent, opts: PostOpts) {
rt = shortenLinks(rt)
rt = stripInvalidMentions(rt)

// add quote embed if present
if (opts.quote) {
embed = {
$type: 'app.bsky.embed.record',
record: {
uri: opts.quote.uri,
cid: opts.quote.cid,
},
} as AppBskyEmbedRecord.Main
}

// add image embed if present
if (opts.images?.length) {
logger.debug(`Uploading images`, {
count: opts.images.length,
})

const images: AppBskyEmbedImages.Image[] = []
for (const image of opts.images) {
opts.onStateChange?.(`Uploading image #${images.length + 1}...`)

logger.debug(`Compressing image`)
const {path, width, height, mime} = await compressImage(image)

logger.debug(`Uploading image`)
const res = await uploadBlob(agent, path, mime)

images.push({
image: res.data.blob,
alt: image.alt,
aspectRatio: {width, height},
})
}

if (opts.quote) {
embed = {
$type: 'app.bsky.embed.recordWithMedia',
record: embed,
media: {
$type: 'app.bsky.embed.images',
images,
},
} as AppBskyEmbedRecordWithMedia.Main
} else {
embed = {
$type: 'app.bsky.embed.images',
images,
} as AppBskyEmbedImages.Main
}
}

// add video embed if present
if (opts.video) {
const captions = await Promise.all(
opts.video.captions
.filter(caption => caption.lang !== '')
.map(async caption => {
const {data} = await agent.uploadBlob(caption.file, {
encoding: 'text/vtt',
})
return {lang: caption.lang, file: data.blob}
}),
)
if (opts.quote) {
embed = {
$type: 'app.bsky.embed.recordWithMedia',
record: embed,
media: {
$type: 'app.bsky.embed.video',
video: opts.video.blobRef,
alt: opts.video.altText || undefined,
captions: captions.length === 0 ? undefined : captions,
aspectRatio: opts.video.aspectRatio,
} as AppBskyEmbedVideo.Main,
} as AppBskyEmbedRecordWithMedia.Main
} else {
embed = {
$type: 'app.bsky.embed.video',
video: opts.video.blobRef,
alt: opts.video.altText || undefined,
captions: captions.length === 0 ? undefined : captions,
aspectRatio: opts.video.aspectRatio,
} as AppBskyEmbedVideo.Main
}
}

// add external embed if present
if (opts.extLink && !opts.images?.length) {
if (opts.extLink.embed) {
embed = opts.extLink.embed
} else {
let thumb
if (opts.extLink.localThumb) {
opts.onStateChange?.('Uploading link thumbnail...')

const {path, mime} = opts.extLink.localThumb.source
const res = await uploadBlob(agent, path, mime)

thumb = res.data.blob
}

if (opts.quote) {
embed = {
$type: 'app.bsky.embed.recordWithMedia',
record: embed,
media: {
$type: 'app.bsky.embed.external',
external: {
uri: opts.extLink.uri,
title: opts.extLink.meta?.title || '',
description: opts.extLink.meta?.description || '',
thumb,
},
} as AppBskyEmbedExternal.Main,
} as AppBskyEmbedRecordWithMedia.Main
} else {
embed = {
$type: 'app.bsky.embed.external',
external: {
uri: opts.extLink.uri,
title: opts.extLink.meta?.title || '',
description: opts.extLink.meta?.description || '',
thumb,
},
} as AppBskyEmbedExternal.Main
}
}
}
const embed = await resolveEmbed(agent, opts)

// add replyTo if post is a reply to another post
if (opts.replyTo) {
Expand Down Expand Up @@ -313,3 +179,117 @@ export async function post(agent: BskyAgent, opts: PostOpts) {

return res
}

async function resolveEmbed(
agent: BskyAgent,
opts: PostOpts,
): Promise<
| AppBskyEmbedImages.Main
| AppBskyEmbedVideo.Main
| AppBskyEmbedExternal.Main
| AppBskyEmbedRecord.Main
| AppBskyEmbedRecordWithMedia.Main
| undefined
> {
const media = await resolveMedia(agent, opts)
if (opts.quote) {
const quoteRecord = {
$type: 'app.bsky.embed.record',
record: {
uri: opts.quote.uri,
cid: opts.quote.cid,
},
}
if (media) {
return {
$type: 'app.bsky.embed.recordWithMedia',
record: quoteRecord,
media,
}
} else {
return quoteRecord
}
}
if (media) {
return media
}
if (opts.extLink?.embed) {
return opts.extLink.embed
}
return undefined
}

async function resolveMedia(
agent: BskyAgent,
opts: PostOpts,
): Promise<
| AppBskyEmbedExternal.Main
| AppBskyEmbedImages.Main
| AppBskyEmbedVideo.Main
| undefined
> {
if (opts.images?.length) {
logger.debug(`Uploading images`, {
count: opts.images.length,
})
opts.onStateChange?.(`Uploading images...`)
const images: AppBskyEmbedImages.Image[] = await Promise.all(
opts.images.map(async (image, i) => {
logger.debug(`Compressing image #${i}`)
const {path, width, height, mime} = await compressImage(image)
logger.debug(`Uploading image #${i}`)
const res = await uploadBlob(agent, path, mime)
return {
image: res.data.blob,
alt: image.alt,
aspectRatio: {width, height},
}
}),
)
return {
$type: 'app.bsky.embed.images',
images,
}
}
if (opts.video) {
const captions = await Promise.all(
opts.video.captions
.filter(caption => caption.lang !== '')
.map(async caption => {
const {data} = await agent.uploadBlob(caption.file, {
encoding: 'text/vtt',
})
return {lang: caption.lang, file: data.blob}
}),
)
return {
$type: 'app.bsky.embed.video',
video: opts.video.blobRef,
alt: opts.video.altText || undefined,
captions: captions.length === 0 ? undefined : captions,
aspectRatio: opts.video.aspectRatio,
}
}
if (opts.extLink) {
if (opts.extLink.embed) {
return undefined
}
let thumb
if (opts.extLink.localThumb) {
opts.onStateChange?.('Uploading link thumbnail...')
const {path, mime} = opts.extLink.localThumb.source
const res = await uploadBlob(agent, path, mime)
thumb = res.data.blob
}
return {
$type: 'app.bsky.embed.external',
external: {
uri: opts.extLink.uri,
title: opts.extLink.meta?.title || '',
description: opts.extLink.meta?.description || '',
thumb,
},
}
}
return undefined
}

0 comments on commit a7ee561

Please sign in to comment.