Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Paralellize image uploads (#5535)

* 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

authored by

dan and committed by
GitHub
a7ee561e b59d6dfb

+117 -137
+117 -137
src/lib/api/index.ts
··· 13 13 RichText, 14 14 } from '@atproto/api' 15 15 16 + import {isNetworkError} from '#/lib/strings/errors' 17 + import {shortenLinks, stripInvalidMentions} from '#/lib/strings/rich-text-manip' 16 18 import {logger} from '#/logger' 17 19 import {ComposerImage, compressImage} from '#/state/gallery' 18 20 import {writePostgateRecord} from '#/state/queries/postgate' ··· 22 24 threadgateAllowUISettingToAllowRecordValue, 23 25 writeThreadgateRecord, 24 26 } from '#/state/queries/threadgate' 25 - import {isNetworkError} from 'lib/strings/errors' 26 - import {shortenLinks, stripInvalidMentions} from 'lib/strings/rich-text-manip' 27 27 import {LinkMeta} from '../link-meta/link-meta' 28 28 import {uploadBlob} from './upload-blob' 29 29 ··· 60 60 } 61 61 62 62 export async function post(agent: BskyAgent, opts: PostOpts) { 63 - let embed: 64 - | AppBskyEmbedImages.Main 65 - | AppBskyEmbedExternal.Main 66 - | AppBskyEmbedRecord.Main 67 - | AppBskyEmbedVideo.Main 68 - | AppBskyEmbedRecordWithMedia.Main 69 - | undefined 70 63 let reply 71 64 let rt = new RichText({text: opts.rawText.trimEnd()}, {cleanNewlines: true}) 72 65 ··· 77 70 rt = shortenLinks(rt) 78 71 rt = stripInvalidMentions(rt) 79 72 80 - // add quote embed if present 81 - if (opts.quote) { 82 - embed = { 83 - $type: 'app.bsky.embed.record', 84 - record: { 85 - uri: opts.quote.uri, 86 - cid: opts.quote.cid, 87 - }, 88 - } as AppBskyEmbedRecord.Main 89 - } 90 - 91 - // add image embed if present 92 - if (opts.images?.length) { 93 - logger.debug(`Uploading images`, { 94 - count: opts.images.length, 95 - }) 96 - 97 - const images: AppBskyEmbedImages.Image[] = [] 98 - for (const image of opts.images) { 99 - opts.onStateChange?.(`Uploading image #${images.length + 1}...`) 100 - 101 - logger.debug(`Compressing image`) 102 - const {path, width, height, mime} = await compressImage(image) 103 - 104 - logger.debug(`Uploading image`) 105 - const res = await uploadBlob(agent, path, mime) 106 - 107 - images.push({ 108 - image: res.data.blob, 109 - alt: image.alt, 110 - aspectRatio: {width, height}, 111 - }) 112 - } 113 - 114 - if (opts.quote) { 115 - embed = { 116 - $type: 'app.bsky.embed.recordWithMedia', 117 - record: embed, 118 - media: { 119 - $type: 'app.bsky.embed.images', 120 - images, 121 - }, 122 - } as AppBskyEmbedRecordWithMedia.Main 123 - } else { 124 - embed = { 125 - $type: 'app.bsky.embed.images', 126 - images, 127 - } as AppBskyEmbedImages.Main 128 - } 129 - } 130 - 131 - // add video embed if present 132 - if (opts.video) { 133 - const captions = await Promise.all( 134 - opts.video.captions 135 - .filter(caption => caption.lang !== '') 136 - .map(async caption => { 137 - const {data} = await agent.uploadBlob(caption.file, { 138 - encoding: 'text/vtt', 139 - }) 140 - return {lang: caption.lang, file: data.blob} 141 - }), 142 - ) 143 - if (opts.quote) { 144 - embed = { 145 - $type: 'app.bsky.embed.recordWithMedia', 146 - record: embed, 147 - media: { 148 - $type: 'app.bsky.embed.video', 149 - video: opts.video.blobRef, 150 - alt: opts.video.altText || undefined, 151 - captions: captions.length === 0 ? undefined : captions, 152 - aspectRatio: opts.video.aspectRatio, 153 - } as AppBskyEmbedVideo.Main, 154 - } as AppBskyEmbedRecordWithMedia.Main 155 - } else { 156 - embed = { 157 - $type: 'app.bsky.embed.video', 158 - video: opts.video.blobRef, 159 - alt: opts.video.altText || undefined, 160 - captions: captions.length === 0 ? undefined : captions, 161 - aspectRatio: opts.video.aspectRatio, 162 - } as AppBskyEmbedVideo.Main 163 - } 164 - } 165 - 166 - // add external embed if present 167 - if (opts.extLink && !opts.images?.length) { 168 - if (opts.extLink.embed) { 169 - embed = opts.extLink.embed 170 - } else { 171 - let thumb 172 - if (opts.extLink.localThumb) { 173 - opts.onStateChange?.('Uploading link thumbnail...') 174 - 175 - const {path, mime} = opts.extLink.localThumb.source 176 - const res = await uploadBlob(agent, path, mime) 177 - 178 - thumb = res.data.blob 179 - } 180 - 181 - if (opts.quote) { 182 - embed = { 183 - $type: 'app.bsky.embed.recordWithMedia', 184 - record: embed, 185 - media: { 186 - $type: 'app.bsky.embed.external', 187 - external: { 188 - uri: opts.extLink.uri, 189 - title: opts.extLink.meta?.title || '', 190 - description: opts.extLink.meta?.description || '', 191 - thumb, 192 - }, 193 - } as AppBskyEmbedExternal.Main, 194 - } as AppBskyEmbedRecordWithMedia.Main 195 - } else { 196 - embed = { 197 - $type: 'app.bsky.embed.external', 198 - external: { 199 - uri: opts.extLink.uri, 200 - title: opts.extLink.meta?.title || '', 201 - description: opts.extLink.meta?.description || '', 202 - thumb, 203 - }, 204 - } as AppBskyEmbedExternal.Main 205 - } 206 - } 207 - } 73 + const embed = await resolveEmbed(agent, opts) 208 74 209 75 // add replyTo if post is a reply to another post 210 76 if (opts.replyTo) { ··· 313 179 314 180 return res 315 181 } 182 + 183 + async function resolveEmbed( 184 + agent: BskyAgent, 185 + opts: PostOpts, 186 + ): Promise< 187 + | AppBskyEmbedImages.Main 188 + | AppBskyEmbedVideo.Main 189 + | AppBskyEmbedExternal.Main 190 + | AppBskyEmbedRecord.Main 191 + | AppBskyEmbedRecordWithMedia.Main 192 + | undefined 193 + > { 194 + const media = await resolveMedia(agent, opts) 195 + if (opts.quote) { 196 + const quoteRecord = { 197 + $type: 'app.bsky.embed.record', 198 + record: { 199 + uri: opts.quote.uri, 200 + cid: opts.quote.cid, 201 + }, 202 + } 203 + if (media) { 204 + return { 205 + $type: 'app.bsky.embed.recordWithMedia', 206 + record: quoteRecord, 207 + media, 208 + } 209 + } else { 210 + return quoteRecord 211 + } 212 + } 213 + if (media) { 214 + return media 215 + } 216 + if (opts.extLink?.embed) { 217 + return opts.extLink.embed 218 + } 219 + return undefined 220 + } 221 + 222 + async function resolveMedia( 223 + agent: BskyAgent, 224 + opts: PostOpts, 225 + ): Promise< 226 + | AppBskyEmbedExternal.Main 227 + | AppBskyEmbedImages.Main 228 + | AppBskyEmbedVideo.Main 229 + | undefined 230 + > { 231 + if (opts.images?.length) { 232 + logger.debug(`Uploading images`, { 233 + count: opts.images.length, 234 + }) 235 + opts.onStateChange?.(`Uploading images...`) 236 + const images: AppBskyEmbedImages.Image[] = await Promise.all( 237 + opts.images.map(async (image, i) => { 238 + logger.debug(`Compressing image #${i}`) 239 + const {path, width, height, mime} = await compressImage(image) 240 + logger.debug(`Uploading image #${i}`) 241 + const res = await uploadBlob(agent, path, mime) 242 + return { 243 + image: res.data.blob, 244 + alt: image.alt, 245 + aspectRatio: {width, height}, 246 + } 247 + }), 248 + ) 249 + return { 250 + $type: 'app.bsky.embed.images', 251 + images, 252 + } 253 + } 254 + if (opts.video) { 255 + const captions = await Promise.all( 256 + opts.video.captions 257 + .filter(caption => caption.lang !== '') 258 + .map(async caption => { 259 + const {data} = await agent.uploadBlob(caption.file, { 260 + encoding: 'text/vtt', 261 + }) 262 + return {lang: caption.lang, file: data.blob} 263 + }), 264 + ) 265 + return { 266 + $type: 'app.bsky.embed.video', 267 + video: opts.video.blobRef, 268 + alt: opts.video.altText || undefined, 269 + captions: captions.length === 0 ? undefined : captions, 270 + aspectRatio: opts.video.aspectRatio, 271 + } 272 + } 273 + if (opts.extLink) { 274 + if (opts.extLink.embed) { 275 + return undefined 276 + } 277 + let thumb 278 + if (opts.extLink.localThumb) { 279 + opts.onStateChange?.('Uploading link thumbnail...') 280 + const {path, mime} = opts.extLink.localThumb.source 281 + const res = await uploadBlob(agent, path, mime) 282 + thumb = res.data.blob 283 + } 284 + return { 285 + $type: 'app.bsky.embed.external', 286 + external: { 287 + uri: opts.extLink.uri, 288 + title: opts.extLink.meta?.title || '', 289 + description: opts.extLink.meta?.description || '', 290 + thumb, 291 + }, 292 + } 293 + } 294 + return undefined 295 + }