Skip to content

Commit d616cfc

Browse files
committed
chore(protos): update flipcash protobuf definitions
Migrate media model from MediaId/MediaMetadata to blob-based MediaItemRendition with role-typed renditions (ORIGINAL, DISPLAY, THUMBNAIL) backed by BlobId/BlobMetadata. Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
1 parent ea5b48b commit d616cfc

9 files changed

Lines changed: 173 additions & 47 deletions

File tree

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
syntax = "proto3";
2+
3+
package flipcash.blob.v1;
4+
5+
option go_package = "github.com/code-payments/flipcash2-protobuf-api/generated/go/blob/v1;blobpb";
6+
option java_package = "com.codeinc.flipcash.gen.blob.v1";
7+
option objc_class_prefix = "FPBBlobV1";
8+
9+
import "validate/validate.proto";
10+
11+
// Opaque, client-held handle to a stored blob. This is the durable identity for
12+
// the bytes; the bytes it points at are immutable once the upload is finalized.
13+
message BlobId {
14+
bytes value = 1 [(validate.rules).bytes = {
15+
min_len: 16
16+
max_len: 16
17+
}];
18+
}
19+
20+
// Server-authoritative metadata describing a stored blob. Never set by clients.
21+
// With the exception of download_url, every field is intrinsic to the stored
22+
// bytes and immutable, derived once by the server.
23+
message BlobMetadata {
24+
// MIME type (e.g. "image/jpeg").
25+
string mime_type = 1 [(validate.rules).string = {
26+
min_len: 1
27+
max_len: 255
28+
}];
29+
30+
// Total size of the blob in bytes.
31+
uint64 size_bytes = 2 [(validate.rules).uint64.gte = 1];
32+
33+
// Ephemeral, server-minted URL for fetching the blob bytes. Unlike the
34+
// other fields it is NOT intrinsic to the blob: it is re-issued on every
35+
// fetch, may expire (signed URL with a short TTL), and is authorized at
36+
// mint time, not at fetch time. Clients MUST NOT persist or cache it across
37+
// fetches; treat the BlobId as the durable handle and this as disposable.
38+
string download_url = 3 [(validate.rules).string = {
39+
uri: true
40+
max_len: 2048
41+
}];
42+
43+
// Kind-specific metadata the server derived from the bytes. Exactly one
44+
// variant is set for a recognized media kind; left unset for opaque blobs.
45+
// Only images are supported today; video/audio/etc. will be added as new
46+
// variants.
47+
oneof kind {
48+
ImageMetadata image = 4;
49+
}
50+
}
51+
52+
// Intrinsic descriptors for a still image.
53+
message ImageMetadata {
54+
// Pixel dimensions, for reserving layout before the bytes arrive.
55+
uint32 width = 1 [(validate.rules).uint32.gte = 1];
56+
uint32 height = 2 [(validate.rules).uint32.gte = 1];
57+
58+
// Compact preview shown while the full image downloads (BlurHash string).
59+
string blurhash = 3 [(validate.rules).string.max_len = 64];
60+
}

definitions/flipcash/protos/src/main/proto/messaging/v1/model.proto

Lines changed: 31 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ option go_package = "github.com/code-payments/flipcash2-protobuf-api/generated/g
66
option java_package = "com.codeinc.flipcash.gen.messaging.v1";
77
option objc_class_prefix = "FPBMessagingV1";
88

9+
import "blob/v1/model.proto";
910
import "common/v1/common.proto";
1011
import "google/protobuf/timestamp.proto";
1112
import "validate/validate.proto";
@@ -140,9 +141,12 @@ message ReplyContent {
140141
}];
141142
}
142143

143-
// Media content (images, video, etc.)
144+
// Media content from blobs the user has already uploaded. The following media
145+
// types are supported:
146+
// - Images
144147
message MediaContent {
145-
// The media items attached to this message
148+
// The media items attached to this message. A single item today; raising
149+
// this cap later enables albums (each item self-describes its kind).
146150
repeated MediaItem items = 1 [(validate.rules).repeated = {
147151
min_items: 1
148152
max_items: 1
@@ -152,43 +156,38 @@ message MediaContent {
152156
TextContent caption = 2;
153157
}
154158

159+
// One logical media item, carried as its set of renditions (quality/size variants).
155160
message MediaItem {
156-
// Client-provided reference to media already uploaded out-of-band
157-
MediaId media_id = 1 [(validate.rules).message.required = true];
158-
159-
// Server-authoritative metadata, resolved from the upload record. It is
160-
// omitted on SendMessage and populated on stored/returned messages
161-
MediaMetadata metadata = 2;
162-
}
163-
164-
message MediaId {
165-
bytes value = 1 [(validate.rules).bytes = {
166-
min_len: 16
167-
max_len: 16
161+
// The renditions of this media, each an independently-stored blob. On
162+
// SendMessage the client supplies exactly one ORIGINAL rendition (its
163+
// blob_id); the server fills metadata and appends any derived renditions
164+
// (e.g. a downscaled DISPLAY and a THUMBNAIL).
165+
repeated MediaItemRendition renditions = 1 [(validate.rules).repeated = {
166+
min_items: 1
168167
}];
169168
}
170169

171-
// Server-authoritative metadata describing an uploaded media item. Never set by
172-
// clients; the server derives every field from the uploaded bytes.
173-
message MediaMetadata {
174-
// MIME type (e.g. "image/jpeg", "video/mp4")
175-
string mime_type = 1 [(validate.rules).string = {
176-
min_len: 1
177-
max_len: 255
170+
// A single stored variant of a MediaItem
171+
message MediaItemRendition {
172+
// The intended use of this rendition within the item.
173+
Role role = 1 [(validate.rules).enum = {
174+
not_in: [0]
178175
}];
176+
enum Role {
177+
UNKNOWN = 0;
178+
ORIGINAL = 1; // full-quality source the client uploaded
179+
DISPLAY = 2; // downscaled/compressed for inline display
180+
THUMBNAIL = 3; // tiny grid preview
181+
}
179182

180-
// Total size of the media in bytes.
181-
uint64 size_bytes = 2 [(validate.rules).uint64.gte = 1];
182-
183-
// Pixel dimensions, for reserving layout before the bytes arrive.
184-
uint32 width = 3 [(validate.rules).uint32.gte = 1];
185-
uint32 height = 4 [(validate.rules).uint32.gte = 1];
186-
187-
// Compact preview shown while the full media downloads (BlurHash string).
188-
string blurhash = 5 [(validate.rules).string.max_len = 64];
183+
// Handle to the blob holding this rendition's bytes. Client-set on the
184+
// ORIGINAL at send time; server-set for derived renditions.
185+
blob.v1.BlobId blob_id = 2 [(validate.rules).message.required = true];
189186

190-
// Duration in milliseconds for audio/video; 0 for stills.
191-
uint64 duration_ms = 6;
187+
// Server-authoritative blob metadata (mime type, size, download URL, and
188+
// the image dimensions/preview), resolved from the blob record. Omitted on
189+
// SendMessage and populated on stored/returned messages.
190+
blob.v1.BlobMetadata blob = 3;
192191
}
193192

194193
// System message content

services/flipcash/src/main/kotlin/com/flipcash/services/internal/network/extensions/LocalToProtobuf.kt

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,10 +151,29 @@ internal fun MessageContent.asContent(): MessagingModel.Content {
151151

152152
internal fun com.flipcash.services.models.chat.MediaItem.asMediaItem(): MessagingModel.MediaItem {
153153
return MessagingModel.MediaItem.newBuilder()
154-
.setMediaId(MessagingModel.MediaId.newBuilder().setValue(mediaId.bytes.toByteString()))
154+
.addAllRenditions(renditions.map { it.asRendition() })
155155
.build()
156156
}
157157

158+
internal fun com.flipcash.services.models.chat.MediaItemRendition.asRendition(): MessagingModel.MediaItemRendition {
159+
return MessagingModel.MediaItemRendition.newBuilder()
160+
.setRole(role.asProtoRole())
161+
.setBlobId(
162+
com.codeinc.flipcash.gen.blob.v1.Model.BlobId.newBuilder()
163+
.setValue(blobId.bytes.toByteString())
164+
)
165+
.build()
166+
}
167+
168+
internal fun com.flipcash.services.models.chat.MediaItemRendition.Role.asProtoRole(): MessagingModel.MediaItemRendition.Role {
169+
return when (this) {
170+
com.flipcash.services.models.chat.MediaItemRendition.Role.ORIGINAL -> MessagingModel.MediaItemRendition.Role.ORIGINAL
171+
com.flipcash.services.models.chat.MediaItemRendition.Role.DISPLAY -> MessagingModel.MediaItemRendition.Role.DISPLAY
172+
com.flipcash.services.models.chat.MediaItemRendition.Role.THUMBNAIL -> MessagingModel.MediaItemRendition.Role.THUMBNAIL
173+
com.flipcash.services.models.chat.MediaItemRendition.Role.UNKNOWN -> MessagingModel.MediaItemRendition.Role.UNKNOWN
174+
}
175+
}
176+
158177
internal fun com.flipcash.services.models.chat.Emoji.asEmoji(): MessagingModel.Emoji {
159178
return MessagingModel.Emoji.newBuilder().setValue(value).build()
160179
}

services/flipcash/src/main/kotlin/com/flipcash/services/internal/network/extensions/ProtobufToLocal.kt

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@ import com.flipcash.services.models.chat.ChatType
2323
import com.flipcash.services.models.chat.ChatUpdate
2424
import com.flipcash.services.models.chat.Emoji
2525
import com.flipcash.services.models.chat.EmojiReaction
26-
import com.flipcash.services.models.chat.MediaId
26+
import com.flipcash.services.models.chat.BlobId
27+
import com.flipcash.services.models.chat.BlobMetadata
28+
import com.flipcash.services.models.chat.ImageMetadata
2729
import com.flipcash.services.models.chat.MediaItem
28-
import com.flipcash.services.models.chat.MediaMetadata
30+
import com.flipcash.services.models.chat.MediaItemRendition
2931
import com.flipcash.services.models.chat.MessageContent
3032
import com.flipcash.services.models.chat.MessagePointer
3133
import com.flipcash.services.models.chat.MetadataUpdate
@@ -145,19 +147,41 @@ internal fun MessagingModel.Content.toMessageContent(): MessageContent {
145147

146148
internal fun MessagingModel.MediaItem.toMediaItem(): MediaItem {
147149
return MediaItem(
148-
mediaId = MediaId(mediaId.value.toByteArray()),
149-
metadata = if (hasMetadata()) metadata.toMediaMetadata() else null,
150+
renditions = renditionsList.map { it.toMediaItemRendition() },
150151
)
151152
}
152153

153-
internal fun MessagingModel.MediaMetadata.toMediaMetadata(): MediaMetadata {
154-
return MediaMetadata(
154+
internal fun MessagingModel.MediaItemRendition.toMediaItemRendition(): MediaItemRendition {
155+
return MediaItemRendition(
156+
role = role.toRole(),
157+
blobId = BlobId(blobId.value.toByteArray()),
158+
blob = if (hasBlob()) blob.toBlobMetadata() else null,
159+
)
160+
}
161+
162+
internal fun MessagingModel.MediaItemRendition.Role.toRole(): MediaItemRendition.Role {
163+
return when (this) {
164+
MessagingModel.MediaItemRendition.Role.ORIGINAL -> MediaItemRendition.Role.ORIGINAL
165+
MessagingModel.MediaItemRendition.Role.DISPLAY -> MediaItemRendition.Role.DISPLAY
166+
MessagingModel.MediaItemRendition.Role.THUMBNAIL -> MediaItemRendition.Role.THUMBNAIL
167+
else -> MediaItemRendition.Role.UNKNOWN
168+
}
169+
}
170+
171+
internal fun com.codeinc.flipcash.gen.blob.v1.Model.BlobMetadata.toBlobMetadata(): BlobMetadata {
172+
return BlobMetadata(
155173
mimeType = mimeType,
156174
sizeBytes = sizeBytes,
175+
downloadUrl = downloadUrl,
176+
image = if (hasImage()) image.toImageMetadata() else null,
177+
)
178+
}
179+
180+
internal fun com.codeinc.flipcash.gen.blob.v1.Model.ImageMetadata.toImageMetadata(): ImageMetadata {
181+
return ImageMetadata(
157182
width = width,
158183
height = height,
159184
blurhash = blurhash,
160-
durationMs = durationMs,
161185
)
162186
}
163187

services/flipcash/src/main/kotlin/com/flipcash/services/models/chat/MediaId.kt renamed to services/flipcash/src/main/kotlin/com/flipcash/services/models/chat/BlobId.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ import kotlinx.serialization.Serializable
44

55
@Serializable
66
@JvmInline
7-
value class MediaId(val bytes: ByteArray)
7+
value class BlobId(val bytes: ByteArray)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.flipcash.services.models.chat
2+
3+
import kotlinx.serialization.Serializable
4+
5+
@Serializable
6+
data class BlobMetadata(
7+
val mimeType: String,
8+
val sizeBytes: Long,
9+
val downloadUrl: String,
10+
val image: ImageMetadata?,
11+
)

services/flipcash/src/main/kotlin/com/flipcash/services/models/chat/MediaMetadata.kt renamed to services/flipcash/src/main/kotlin/com/flipcash/services/models/chat/ImageMetadata.kt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,8 @@ package com.flipcash.services.models.chat
33
import kotlinx.serialization.Serializable
44

55
@Serializable
6-
data class MediaMetadata(
7-
val mimeType: String,
8-
val sizeBytes: Long,
6+
data class ImageMetadata(
97
val width: Int,
108
val height: Int,
119
val blurhash: String,
12-
val durationMs: Long,
1310
)

services/flipcash/src/main/kotlin/com/flipcash/services/models/chat/MediaItem.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,5 @@ import kotlinx.serialization.Serializable
44

55
@Serializable
66
data class MediaItem(
7-
val mediaId: MediaId,
8-
val metadata: MediaMetadata?,
7+
val renditions: List<MediaItemRendition>,
98
)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.flipcash.services.models.chat
2+
3+
import kotlinx.serialization.Serializable
4+
5+
@Serializable
6+
data class MediaItemRendition(
7+
val role: Role,
8+
val blobId: BlobId,
9+
val blob: BlobMetadata?,
10+
) {
11+
enum class Role {
12+
UNKNOWN,
13+
ORIGINAL,
14+
DISPLAY,
15+
THUMBNAIL,
16+
}
17+
}

0 commit comments

Comments
 (0)