diff --git a/README.md b/README.md index 2915db6..272a509 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,11 @@ Create a conversion request: ```bash curl \ --header "Content-Type: application/json" \ - --data '{"sourceFilename":"example.pptx","resolution":"CONVERSION_RESOLUTION_FHD"}' \ + --data '{ + "sourceFilename":"example.pptx", + "full":{"resolution":"CONVERSION_RESOLUTION_FHD","jpeg":{"quality":85}}, + "thumbnail":{"resolution":"CONVERSION_RESOLUTION_SD","jpeg":{"quality":75}} + }' \ http://localhost:8080/officeconvertapi.v1.ConversionService/CreateConversion ``` @@ -139,8 +143,10 @@ Use `.env.example` as your baseline env configuration. If conversion fails on larger decks, tune these environment variables: -- `CreateConversionRequest.resolution` controls output dimensions via presets: `SD`, `HD`, `FHD`, `QHD`, `UHD`. -- Omitting `resolution` (or sending `CONVERSION_RESOLUTION_UNSPECIFIED`) defaults to `FHD`. -- Rasterization DPI is inferred automatically from source slide size and selected output dimensions. +- `CreateConversionRequest.full.resolution` controls full-size output dimensions via presets: `SD`, `HD`, `FHD`, `QHD`, `UHD`. +- `CreateConversionRequest.thumbnail.resolution` controls thumbnail output dimensions with the same presets. +- Omitting full/thumbnail resolution (or sending `CONVERSION_RESOLUTION_UNSPECIFIED`) defaults to `FHD` for full and `SD` for thumbnail. +- Output is JPEG-only for now; set `CreateConversionRequest.full.jpeg.quality` and `CreateConversionRequest.thumbnail.jpeg.quality` to `1..100` (`0` or omitted uses server defaults: full `85`, thumbnail `75`). +- Rasterization DPI is inferred automatically from source slide size and selected full/thumbnail output dimensions. - `CONVERSION_PPTX_TO_PDF_TIMEOUT_SECONDS` (default `180`): timeout for LibreOffice export. - `CONVERSION_PDF_TO_IMAGES_TIMEOUT_SECONDS` (default `1800`): timeout for Poppler rasterization. diff --git a/clients/go/officeconvertclient/client.go b/clients/go/officeconvertclient/client.go index 76398e1..b5c94ce 100644 --- a/clients/go/officeconvertclient/client.go +++ b/clients/go/officeconvertclient/client.go @@ -8,13 +8,25 @@ import ( "time" "connectrpc.com/connect" - "github.com/segmentio/ksuid" officeconvertapiv1 "gitea.auvem.com/end-internal/officeconvert/gen/go/officeconvertapi/v1" "gitea.auvem.com/end-internal/officeconvert/gen/go/officeconvertapi/v1/officeconvertapiv1connect" + "github.com/segmentio/ksuid" ) const defaultPollInterval = 2 * time.Second +// RasterTierOptions defines per-tier raster settings for conversion output. +type RasterTierOptions struct { + Resolution officeconvertapiv1.ConversionResolution + JPEGQuality int32 +} + +// CreateConversionOptions configures optional full and thumbnail raster tiers. +type CreateConversionOptions struct { + Full *RasterTierOptions + Thumbnail *RasterTierOptions +} + // Client wraps the generated Connect client with orchestration-focused helpers. type Client struct { rpc officeconvertapiv1connect.ConversionServiceClient @@ -47,12 +59,16 @@ func (c *Client) SetPollInterval(interval time.Duration) { func (c *Client) CreateConversion( ctx context.Context, sourceFilename string, - resolution officeconvertapiv1.ConversionResolution, + options *CreateConversionOptions, ) (*CreateConversionResponse, error) { - req := connect.NewRequest(&officeconvertapiv1.CreateConversionRequest{ + message := &officeconvertapiv1.CreateConversionRequest{ SourceFilename: sourceFilename, - Resolution: resolution, - }) + } + if options != nil { + message.Full = toProtoSlideRasterOptions(options.Full) + message.Thumbnail = toProtoSlideRasterOptions(options.Thumbnail) + } + req := connect.NewRequest(message) res, err := c.rpc.CreateConversion(ctx, req) if err != nil { return nil, err @@ -60,6 +76,25 @@ func (c *Client) CreateConversion( return augmentCreateConversionResponse(res.Msg) } +func toProtoSlideRasterOptions( + options *RasterTierOptions, +) *officeconvertapiv1.SlideRasterOptions { + if options == nil { + return nil + } + proto := &officeconvertapiv1.SlideRasterOptions{ + Resolution: options.Resolution, + } + if options.JPEGQuality != 0 { + proto.Format = &officeconvertapiv1.SlideRasterOptions_Jpeg{ + Jpeg: &officeconvertapiv1.JpegOutputOptions{ + Quality: options.JPEGQuality, + }, + } + } + return proto +} + // StartConversion signals that upload is complete and conversion can begin. func (c *Client) StartConversion( ctx context.Context, diff --git a/clients/go/officeconvertclient/orchestrate.go b/clients/go/officeconvertclient/orchestrate.go index 87e7ab7..d705d7e 100644 --- a/clients/go/officeconvertclient/orchestrate.go +++ b/clients/go/officeconvertclient/orchestrate.go @@ -19,7 +19,7 @@ func (c *Client) ConvertPPTXFile(ctx context.Context, localPPTXPath string) (*Co createRes, err := c.CreateConversion( ctx, filepath.Base(localPPTXPath), - officeconvertapiv1.ConversionResolution_CONVERSION_RESOLUTION_UNSPECIFIED, + nil, ) if err != nil { return nil, fmt.Errorf("create conversion: %w", err) diff --git a/gen/go/officeconvertapi/v1/conversion.pb.go b/gen/go/officeconvertapi/v1/conversion.pb.go index 0135ccb..32393d7 100644 --- a/gen/go/officeconvertapi/v1/conversion.pb.go +++ b/gen/go/officeconvertapi/v1/conversion.pb.go @@ -196,19 +196,144 @@ func (ConversionResolution) EnumDescriptor() ([]byte, []int) { return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{2} } -// Slide contains extracted notes and the rendered image URL for one slide. -type Slide struct { - state protoimpl.MessageState `protogen:"open.v1"` - Index int32 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` - NotesPlain string `protobuf:"bytes,2,opt,name=notes_plain,json=notesPlain,proto3" json:"notes_plain,omitempty"` - ImageUrl string `protobuf:"bytes,3,opt,name=image_url,json=imageUrl,proto3" json:"image_url,omitempty"` +// JpegOutputOptions configures JPEG-specific encoding controls. +type JpegOutputOptions struct { + state protoimpl.MessageState `protogen:"open.v1"` + // JPEG quality from 1..100. 0 means server default. + Quality int32 `protobuf:"varint,1,opt,name=quality,proto3" json:"quality,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } +func (x *JpegOutputOptions) Reset() { + *x = JpegOutputOptions{} + mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *JpegOutputOptions) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JpegOutputOptions) ProtoMessage() {} + +func (x *JpegOutputOptions) ProtoReflect() protoreflect.Message { + mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JpegOutputOptions.ProtoReflect.Descriptor instead. +func (*JpegOutputOptions) Descriptor() ([]byte, []int) { + return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{0} +} + +func (x *JpegOutputOptions) GetQuality() int32 { + if x != nil { + return x.Quality + } + return 0 +} + +// SlideRasterOptions defines rendering settings for a raster output tier. +type SlideRasterOptions struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Output resolution preset. UNSPECIFIED means server default for the tier. + Resolution ConversionResolution `protobuf:"varint,1,opt,name=resolution,proto3,enum=officeconvertapi.v1.ConversionResolution" json:"resolution,omitempty"` + // Types that are valid to be assigned to Format: + // + // *SlideRasterOptions_Jpeg + Format isSlideRasterOptions_Format `protobuf_oneof:"format"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SlideRasterOptions) Reset() { + *x = SlideRasterOptions{} + mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SlideRasterOptions) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SlideRasterOptions) ProtoMessage() {} + +func (x *SlideRasterOptions) ProtoReflect() protoreflect.Message { + mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SlideRasterOptions.ProtoReflect.Descriptor instead. +func (*SlideRasterOptions) Descriptor() ([]byte, []int) { + return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{1} +} + +func (x *SlideRasterOptions) GetResolution() ConversionResolution { + if x != nil { + return x.Resolution + } + return ConversionResolution_CONVERSION_RESOLUTION_UNSPECIFIED +} + +func (x *SlideRasterOptions) GetFormat() isSlideRasterOptions_Format { + if x != nil { + return x.Format + } + return nil +} + +func (x *SlideRasterOptions) GetJpeg() *JpegOutputOptions { + if x != nil { + if x, ok := x.Format.(*SlideRasterOptions_Jpeg); ok { + return x.Jpeg + } + } + return nil +} + +type isSlideRasterOptions_Format interface { + isSlideRasterOptions_Format() +} + +type SlideRasterOptions_Jpeg struct { + Jpeg *JpegOutputOptions `protobuf:"bytes,2,opt,name=jpeg,proto3,oneof"` +} + +func (*SlideRasterOptions_Jpeg) isSlideRasterOptions_Format() {} + +// Slide contains extracted notes and the rendered image URL for one slide. +type Slide struct { + state protoimpl.MessageState `protogen:"open.v1"` + Index int32 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` + NotesPlain string `protobuf:"bytes,2,opt,name=notes_plain,json=notesPlain,proto3" json:"notes_plain,omitempty"` + // Full-size rendered image URL. + ImageUrl string `protobuf:"bytes,3,opt,name=image_url,json=imageUrl,proto3" json:"image_url,omitempty"` + // Thumbnail rendered image URL. + ThumbnailImageUrl string `protobuf:"bytes,4,opt,name=thumbnail_image_url,json=thumbnailImageUrl,proto3" json:"thumbnail_image_url,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + func (x *Slide) Reset() { *x = Slide{} - mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[0] + mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -220,7 +345,7 @@ func (x *Slide) String() string { func (*Slide) ProtoMessage() {} func (x *Slide) ProtoReflect() protoreflect.Message { - mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[0] + mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -233,7 +358,7 @@ func (x *Slide) ProtoReflect() protoreflect.Message { // Deprecated: Use Slide.ProtoReflect.Descriptor instead. func (*Slide) Descriptor() ([]byte, []int) { - return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{0} + return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{2} } func (x *Slide) GetIndex() int32 { @@ -257,6 +382,13 @@ func (x *Slide) GetImageUrl() string { return "" } +func (x *Slide) GetThumbnailImageUrl() string { + if x != nil { + return x.ThumbnailImageUrl + } + return "" +} + // SlideDeck is the final structured conversion artifact. type SlideDeck struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -265,15 +397,21 @@ type SlideDeck struct { SourceFilename string `protobuf:"bytes,2,opt,name=source_filename,json=sourceFilename,proto3" json:"source_filename,omitempty"` Slides []*Slide `protobuf:"bytes,3,rep,name=slides,proto3" json:"slides,omitempty"` CreatedAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` - Width int32 `protobuf:"varint,5,opt,name=width,proto3" json:"width,omitempty"` - Height int32 `protobuf:"varint,6,opt,name=height,proto3" json:"height,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // Full-size raster width in pixels. + Width int32 `protobuf:"varint,5,opt,name=width,proto3" json:"width,omitempty"` + // Full-size raster height in pixels. + Height int32 `protobuf:"varint,6,opt,name=height,proto3" json:"height,omitempty"` + // Thumbnail raster width in pixels. + ThumbnailWidth int32 `protobuf:"varint,7,opt,name=thumbnail_width,json=thumbnailWidth,proto3" json:"thumbnail_width,omitempty"` + // Thumbnail raster height in pixels. + ThumbnailHeight int32 `protobuf:"varint,8,opt,name=thumbnail_height,json=thumbnailHeight,proto3" json:"thumbnail_height,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *SlideDeck) Reset() { *x = SlideDeck{} - mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[1] + mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -285,7 +423,7 @@ func (x *SlideDeck) String() string { func (*SlideDeck) ProtoMessage() {} func (x *SlideDeck) ProtoReflect() protoreflect.Message { - mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[1] + mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -298,7 +436,7 @@ func (x *SlideDeck) ProtoReflect() protoreflect.Message { // Deprecated: Use SlideDeck.ProtoReflect.Descriptor instead. func (*SlideDeck) Descriptor() ([]byte, []int) { - return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{1} + return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{3} } func (x *SlideDeck) GetConversionId() string { @@ -343,18 +481,33 @@ func (x *SlideDeck) GetHeight() int32 { return 0 } +func (x *SlideDeck) GetThumbnailWidth() int32 { + if x != nil { + return x.ThumbnailWidth + } + return 0 +} + +func (x *SlideDeck) GetThumbnailHeight() int32 { + if x != nil { + return x.ThumbnailHeight + } + return 0 +} + // CreateConversionRequest starts a conversion session. type CreateConversionRequest struct { state protoimpl.MessageState `protogen:"open.v1"` SourceFilename string `protobuf:"bytes,1,opt,name=source_filename,json=sourceFilename,proto3" json:"source_filename,omitempty"` - Resolution ConversionResolution `protobuf:"varint,2,opt,name=resolution,proto3,enum=officeconvertapi.v1.ConversionResolution" json:"resolution,omitempty"` + Full *SlideRasterOptions `protobuf:"bytes,2,opt,name=full,proto3" json:"full,omitempty"` + Thumbnail *SlideRasterOptions `protobuf:"bytes,3,opt,name=thumbnail,proto3" json:"thumbnail,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *CreateConversionRequest) Reset() { *x = CreateConversionRequest{} - mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[2] + mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -366,7 +519,7 @@ func (x *CreateConversionRequest) String() string { func (*CreateConversionRequest) ProtoMessage() {} func (x *CreateConversionRequest) ProtoReflect() protoreflect.Message { - mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[2] + mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -379,7 +532,7 @@ func (x *CreateConversionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateConversionRequest.ProtoReflect.Descriptor instead. func (*CreateConversionRequest) Descriptor() ([]byte, []int) { - return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{2} + return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{4} } func (x *CreateConversionRequest) GetSourceFilename() string { @@ -389,11 +542,18 @@ func (x *CreateConversionRequest) GetSourceFilename() string { return "" } -func (x *CreateConversionRequest) GetResolution() ConversionResolution { +func (x *CreateConversionRequest) GetFull() *SlideRasterOptions { if x != nil { - return x.Resolution + return x.Full } - return ConversionResolution_CONVERSION_RESOLUTION_UNSPECIFIED + return nil +} + +func (x *CreateConversionRequest) GetThumbnail() *SlideRasterOptions { + if x != nil { + return x.Thumbnail + } + return nil } // CreateConversionResponse returns upload details for the session. @@ -411,7 +571,7 @@ type CreateConversionResponse struct { func (x *CreateConversionResponse) Reset() { *x = CreateConversionResponse{} - mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[3] + mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -423,7 +583,7 @@ func (x *CreateConversionResponse) String() string { func (*CreateConversionResponse) ProtoMessage() {} func (x *CreateConversionResponse) ProtoReflect() protoreflect.Message { - mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[3] + mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -436,7 +596,7 @@ func (x *CreateConversionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateConversionResponse.ProtoReflect.Descriptor instead. func (*CreateConversionResponse) Descriptor() ([]byte, []int) { - return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{3} + return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{5} } func (x *CreateConversionResponse) GetConversionId() string { @@ -485,7 +645,7 @@ type StartConversionRequest struct { func (x *StartConversionRequest) Reset() { *x = StartConversionRequest{} - mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[4] + mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -497,7 +657,7 @@ func (x *StartConversionRequest) String() string { func (*StartConversionRequest) ProtoMessage() {} func (x *StartConversionRequest) ProtoReflect() protoreflect.Message { - mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[4] + mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -510,7 +670,7 @@ func (x *StartConversionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use StartConversionRequest.ProtoReflect.Descriptor instead. func (*StartConversionRequest) Descriptor() ([]byte, []int) { - return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{4} + return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{6} } func (x *StartConversionRequest) GetConversionId() string { @@ -532,7 +692,7 @@ type StartConversionResponse struct { func (x *StartConversionResponse) Reset() { *x = StartConversionResponse{} - mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[5] + mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -544,7 +704,7 @@ func (x *StartConversionResponse) String() string { func (*StartConversionResponse) ProtoMessage() {} func (x *StartConversionResponse) ProtoReflect() protoreflect.Message { - mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[5] + mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -557,7 +717,7 @@ func (x *StartConversionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use StartConversionResponse.ProtoReflect.Descriptor instead. func (*StartConversionResponse) Descriptor() ([]byte, []int) { - return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{5} + return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{7} } func (x *StartConversionResponse) GetConversionId() string { @@ -585,7 +745,7 @@ type GetConversionStatusRequest struct { func (x *GetConversionStatusRequest) Reset() { *x = GetConversionStatusRequest{} - mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[6] + mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -597,7 +757,7 @@ func (x *GetConversionStatusRequest) String() string { func (*GetConversionStatusRequest) ProtoMessage() {} func (x *GetConversionStatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[6] + mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -610,7 +770,7 @@ func (x *GetConversionStatusRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetConversionStatusRequest.ProtoReflect.Descriptor instead. func (*GetConversionStatusRequest) Descriptor() ([]byte, []int) { - return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{6} + return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{8} } func (x *GetConversionStatusRequest) GetConversionId() string { @@ -637,7 +797,7 @@ type GetConversionStatusResponse struct { func (x *GetConversionStatusResponse) Reset() { *x = GetConversionStatusResponse{} - mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[7] + mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -649,7 +809,7 @@ func (x *GetConversionStatusResponse) String() string { func (*GetConversionStatusResponse) ProtoMessage() {} func (x *GetConversionStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[7] + mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -662,7 +822,7 @@ func (x *GetConversionStatusResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetConversionStatusResponse.ProtoReflect.Descriptor instead. func (*GetConversionStatusResponse) Descriptor() ([]byte, []int) { - return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{7} + return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{9} } func (x *GetConversionStatusResponse) GetConversionId() string { @@ -725,7 +885,7 @@ type GetSlideDeckRequest struct { func (x *GetSlideDeckRequest) Reset() { *x = GetSlideDeckRequest{} - mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[8] + mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -737,7 +897,7 @@ func (x *GetSlideDeckRequest) String() string { func (*GetSlideDeckRequest) ProtoMessage() {} func (x *GetSlideDeckRequest) ProtoReflect() protoreflect.Message { - mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[8] + mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -750,7 +910,7 @@ func (x *GetSlideDeckRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSlideDeckRequest.ProtoReflect.Descriptor instead. func (*GetSlideDeckRequest) Descriptor() ([]byte, []int) { - return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{8} + return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{10} } func (x *GetSlideDeckRequest) GetConversionId() string { @@ -770,7 +930,7 @@ type GetSlideDeckResponse struct { func (x *GetSlideDeckResponse) Reset() { *x = GetSlideDeckResponse{} - mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[9] + mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -782,7 +942,7 @@ func (x *GetSlideDeckResponse) String() string { func (*GetSlideDeckResponse) ProtoMessage() {} func (x *GetSlideDeckResponse) ProtoReflect() protoreflect.Message { - mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[9] + mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -795,7 +955,7 @@ func (x *GetSlideDeckResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSlideDeckResponse.ProtoReflect.Descriptor instead. func (*GetSlideDeckResponse) Descriptor() ([]byte, []int) { - return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{9} + return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{11} } func (x *GetSlideDeckResponse) GetSlideDeck() *SlideDeck { @@ -816,7 +976,7 @@ type DeleteConversionRequest struct { func (x *DeleteConversionRequest) Reset() { *x = DeleteConversionRequest{} - mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[10] + mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -828,7 +988,7 @@ func (x *DeleteConversionRequest) String() string { func (*DeleteConversionRequest) ProtoMessage() {} func (x *DeleteConversionRequest) ProtoReflect() protoreflect.Message { - mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[10] + mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -841,7 +1001,7 @@ func (x *DeleteConversionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteConversionRequest.ProtoReflect.Descriptor instead. func (*DeleteConversionRequest) Descriptor() ([]byte, []int) { - return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{10} + return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{12} } func (x *DeleteConversionRequest) GetConversionId() string { @@ -863,7 +1023,7 @@ type DeleteConversionResponse struct { func (x *DeleteConversionResponse) Reset() { *x = DeleteConversionResponse{} - mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[11] + mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -875,7 +1035,7 @@ func (x *DeleteConversionResponse) String() string { func (*DeleteConversionResponse) ProtoMessage() {} func (x *DeleteConversionResponse) ProtoReflect() protoreflect.Message { - mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[11] + mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -888,7 +1048,7 @@ func (x *DeleteConversionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteConversionResponse.ProtoReflect.Descriptor instead. func (*DeleteConversionResponse) Descriptor() ([]byte, []int) { - return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{11} + return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{13} } func (x *DeleteConversionResponse) GetConversionId() string { @@ -909,12 +1069,21 @@ var File_officeconvertapi_v1_conversion_proto protoreflect.FileDescriptor const file_officeconvertapi_v1_conversion_proto_rawDesc = "" + "\n" + - "$officeconvertapi/v1/conversion.proto\x12\x13officeconvertapi.v1\x1a\x1fgoogle/protobuf/timestamp.proto\"[\n" + + "$officeconvertapi/v1/conversion.proto\x12\x13officeconvertapi.v1\x1a\x1fgoogle/protobuf/timestamp.proto\"-\n" + + "\x11JpegOutputOptions\x12\x18\n" + + "\aquality\x18\x01 \x01(\x05R\aquality\"\xa7\x01\n" + + "\x12SlideRasterOptions\x12I\n" + + "\n" + + "resolution\x18\x01 \x01(\x0e2).officeconvertapi.v1.ConversionResolutionR\n" + + "resolution\x12<\n" + + "\x04jpeg\x18\x02 \x01(\v2&.officeconvertapi.v1.JpegOutputOptionsH\x00R\x04jpegB\b\n" + + "\x06format\"\x8b\x01\n" + "\x05Slide\x12\x14\n" + "\x05index\x18\x01 \x01(\x05R\x05index\x12\x1f\n" + "\vnotes_plain\x18\x02 \x01(\tR\n" + "notesPlain\x12\x1b\n" + - "\timage_url\x18\x03 \x01(\tR\bimageUrl\"\xf6\x01\n" + + "\timage_url\x18\x03 \x01(\tR\bimageUrl\x12.\n" + + "\x13thumbnail_image_url\x18\x04 \x01(\tR\x11thumbnailImageUrl\"\xca\x02\n" + "\tSlideDeck\x12#\n" + "\rconversion_id\x18\x01 \x01(\tR\fconversionId\x12'\n" + "\x0fsource_filename\x18\x02 \x01(\tR\x0esourceFilename\x122\n" + @@ -922,12 +1091,13 @@ const file_officeconvertapi_v1_conversion_proto_rawDesc = "" + "\n" + "created_at\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x12\x14\n" + "\x05width\x18\x05 \x01(\x05R\x05width\x12\x16\n" + - "\x06height\x18\x06 \x01(\x05R\x06height\"\x8d\x01\n" + + "\x06height\x18\x06 \x01(\x05R\x06height\x12'\n" + + "\x0fthumbnail_width\x18\a \x01(\x05R\x0ethumbnailWidth\x12)\n" + + "\x10thumbnail_height\x18\b \x01(\x05R\x0fthumbnailHeight\"\xc6\x01\n" + "\x17CreateConversionRequest\x12'\n" + - "\x0fsource_filename\x18\x01 \x01(\tR\x0esourceFilename\x12I\n" + - "\n" + - "resolution\x18\x02 \x01(\x0e2).officeconvertapi.v1.ConversionResolutionR\n" + - "resolution\"\xea\x01\n" + + "\x0fsource_filename\x18\x01 \x01(\tR\x0esourceFilename\x12;\n" + + "\x04full\x18\x02 \x01(\v2'.officeconvertapi.v1.SlideRasterOptionsR\x04full\x12E\n" + + "\tthumbnail\x18\x03 \x01(\v2'.officeconvertapi.v1.SlideRasterOptionsR\tthumbnail\"\xea\x01\n" + "\x18CreateConversionResponse\x12#\n" + "\rconversion_id\x18\x01 \x01(\tR\fconversionId\x12#\n" + "\rupload_bucket\x18\x02 \x01(\tR\fuploadBucket\x12*\n" + @@ -1002,50 +1172,55 @@ func file_officeconvertapi_v1_conversion_proto_rawDescGZIP() []byte { } var file_officeconvertapi_v1_conversion_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_officeconvertapi_v1_conversion_proto_msgTypes = make([]protoimpl.MessageInfo, 12) +var file_officeconvertapi_v1_conversion_proto_msgTypes = make([]protoimpl.MessageInfo, 14) var file_officeconvertapi_v1_conversion_proto_goTypes = []any{ (ConversionStatus)(0), // 0: officeconvertapi.v1.ConversionStatus (ConversionPhase)(0), // 1: officeconvertapi.v1.ConversionPhase (ConversionResolution)(0), // 2: officeconvertapi.v1.ConversionResolution - (*Slide)(nil), // 3: officeconvertapi.v1.Slide - (*SlideDeck)(nil), // 4: officeconvertapi.v1.SlideDeck - (*CreateConversionRequest)(nil), // 5: officeconvertapi.v1.CreateConversionRequest - (*CreateConversionResponse)(nil), // 6: officeconvertapi.v1.CreateConversionResponse - (*StartConversionRequest)(nil), // 7: officeconvertapi.v1.StartConversionRequest - (*StartConversionResponse)(nil), // 8: officeconvertapi.v1.StartConversionResponse - (*GetConversionStatusRequest)(nil), // 9: officeconvertapi.v1.GetConversionStatusRequest - (*GetConversionStatusResponse)(nil), // 10: officeconvertapi.v1.GetConversionStatusResponse - (*GetSlideDeckRequest)(nil), // 11: officeconvertapi.v1.GetSlideDeckRequest - (*GetSlideDeckResponse)(nil), // 12: officeconvertapi.v1.GetSlideDeckResponse - (*DeleteConversionRequest)(nil), // 13: officeconvertapi.v1.DeleteConversionRequest - (*DeleteConversionResponse)(nil), // 14: officeconvertapi.v1.DeleteConversionResponse - (*timestamppb.Timestamp)(nil), // 15: google.protobuf.Timestamp + (*JpegOutputOptions)(nil), // 3: officeconvertapi.v1.JpegOutputOptions + (*SlideRasterOptions)(nil), // 4: officeconvertapi.v1.SlideRasterOptions + (*Slide)(nil), // 5: officeconvertapi.v1.Slide + (*SlideDeck)(nil), // 6: officeconvertapi.v1.SlideDeck + (*CreateConversionRequest)(nil), // 7: officeconvertapi.v1.CreateConversionRequest + (*CreateConversionResponse)(nil), // 8: officeconvertapi.v1.CreateConversionResponse + (*StartConversionRequest)(nil), // 9: officeconvertapi.v1.StartConversionRequest + (*StartConversionResponse)(nil), // 10: officeconvertapi.v1.StartConversionResponse + (*GetConversionStatusRequest)(nil), // 11: officeconvertapi.v1.GetConversionStatusRequest + (*GetConversionStatusResponse)(nil), // 12: officeconvertapi.v1.GetConversionStatusResponse + (*GetSlideDeckRequest)(nil), // 13: officeconvertapi.v1.GetSlideDeckRequest + (*GetSlideDeckResponse)(nil), // 14: officeconvertapi.v1.GetSlideDeckResponse + (*DeleteConversionRequest)(nil), // 15: officeconvertapi.v1.DeleteConversionRequest + (*DeleteConversionResponse)(nil), // 16: officeconvertapi.v1.DeleteConversionResponse + (*timestamppb.Timestamp)(nil), // 17: google.protobuf.Timestamp } var file_officeconvertapi_v1_conversion_proto_depIdxs = []int32{ - 3, // 0: officeconvertapi.v1.SlideDeck.slides:type_name -> officeconvertapi.v1.Slide - 15, // 1: officeconvertapi.v1.SlideDeck.created_at:type_name -> google.protobuf.Timestamp - 2, // 2: officeconvertapi.v1.CreateConversionRequest.resolution:type_name -> officeconvertapi.v1.ConversionResolution - 15, // 3: officeconvertapi.v1.CreateConversionResponse.expires_at:type_name -> google.protobuf.Timestamp - 0, // 4: officeconvertapi.v1.StartConversionResponse.status:type_name -> officeconvertapi.v1.ConversionStatus - 0, // 5: officeconvertapi.v1.GetConversionStatusResponse.status:type_name -> officeconvertapi.v1.ConversionStatus - 15, // 6: officeconvertapi.v1.GetConversionStatusResponse.updated_at:type_name -> google.protobuf.Timestamp - 1, // 7: officeconvertapi.v1.GetConversionStatusResponse.phase:type_name -> officeconvertapi.v1.ConversionPhase - 4, // 8: officeconvertapi.v1.GetSlideDeckResponse.slide_deck:type_name -> officeconvertapi.v1.SlideDeck - 5, // 9: officeconvertapi.v1.ConversionService.CreateConversion:input_type -> officeconvertapi.v1.CreateConversionRequest - 7, // 10: officeconvertapi.v1.ConversionService.StartConversion:input_type -> officeconvertapi.v1.StartConversionRequest - 9, // 11: officeconvertapi.v1.ConversionService.GetConversionStatus:input_type -> officeconvertapi.v1.GetConversionStatusRequest - 11, // 12: officeconvertapi.v1.ConversionService.GetSlideDeck:input_type -> officeconvertapi.v1.GetSlideDeckRequest - 13, // 13: officeconvertapi.v1.ConversionService.DeleteConversion:input_type -> officeconvertapi.v1.DeleteConversionRequest - 6, // 14: officeconvertapi.v1.ConversionService.CreateConversion:output_type -> officeconvertapi.v1.CreateConversionResponse - 8, // 15: officeconvertapi.v1.ConversionService.StartConversion:output_type -> officeconvertapi.v1.StartConversionResponse - 10, // 16: officeconvertapi.v1.ConversionService.GetConversionStatus:output_type -> officeconvertapi.v1.GetConversionStatusResponse - 12, // 17: officeconvertapi.v1.ConversionService.GetSlideDeck:output_type -> officeconvertapi.v1.GetSlideDeckResponse - 14, // 18: officeconvertapi.v1.ConversionService.DeleteConversion:output_type -> officeconvertapi.v1.DeleteConversionResponse - 14, // [14:19] is the sub-list for method output_type - 9, // [9:14] is the sub-list for method input_type - 9, // [9:9] is the sub-list for extension type_name - 9, // [9:9] is the sub-list for extension extendee - 0, // [0:9] is the sub-list for field type_name + 2, // 0: officeconvertapi.v1.SlideRasterOptions.resolution:type_name -> officeconvertapi.v1.ConversionResolution + 3, // 1: officeconvertapi.v1.SlideRasterOptions.jpeg:type_name -> officeconvertapi.v1.JpegOutputOptions + 5, // 2: officeconvertapi.v1.SlideDeck.slides:type_name -> officeconvertapi.v1.Slide + 17, // 3: officeconvertapi.v1.SlideDeck.created_at:type_name -> google.protobuf.Timestamp + 4, // 4: officeconvertapi.v1.CreateConversionRequest.full:type_name -> officeconvertapi.v1.SlideRasterOptions + 4, // 5: officeconvertapi.v1.CreateConversionRequest.thumbnail:type_name -> officeconvertapi.v1.SlideRasterOptions + 17, // 6: officeconvertapi.v1.CreateConversionResponse.expires_at:type_name -> google.protobuf.Timestamp + 0, // 7: officeconvertapi.v1.StartConversionResponse.status:type_name -> officeconvertapi.v1.ConversionStatus + 0, // 8: officeconvertapi.v1.GetConversionStatusResponse.status:type_name -> officeconvertapi.v1.ConversionStatus + 17, // 9: officeconvertapi.v1.GetConversionStatusResponse.updated_at:type_name -> google.protobuf.Timestamp + 1, // 10: officeconvertapi.v1.GetConversionStatusResponse.phase:type_name -> officeconvertapi.v1.ConversionPhase + 6, // 11: officeconvertapi.v1.GetSlideDeckResponse.slide_deck:type_name -> officeconvertapi.v1.SlideDeck + 7, // 12: officeconvertapi.v1.ConversionService.CreateConversion:input_type -> officeconvertapi.v1.CreateConversionRequest + 9, // 13: officeconvertapi.v1.ConversionService.StartConversion:input_type -> officeconvertapi.v1.StartConversionRequest + 11, // 14: officeconvertapi.v1.ConversionService.GetConversionStatus:input_type -> officeconvertapi.v1.GetConversionStatusRequest + 13, // 15: officeconvertapi.v1.ConversionService.GetSlideDeck:input_type -> officeconvertapi.v1.GetSlideDeckRequest + 15, // 16: officeconvertapi.v1.ConversionService.DeleteConversion:input_type -> officeconvertapi.v1.DeleteConversionRequest + 8, // 17: officeconvertapi.v1.ConversionService.CreateConversion:output_type -> officeconvertapi.v1.CreateConversionResponse + 10, // 18: officeconvertapi.v1.ConversionService.StartConversion:output_type -> officeconvertapi.v1.StartConversionResponse + 12, // 19: officeconvertapi.v1.ConversionService.GetConversionStatus:output_type -> officeconvertapi.v1.GetConversionStatusResponse + 14, // 20: officeconvertapi.v1.ConversionService.GetSlideDeck:output_type -> officeconvertapi.v1.GetSlideDeckResponse + 16, // 21: officeconvertapi.v1.ConversionService.DeleteConversion:output_type -> officeconvertapi.v1.DeleteConversionResponse + 17, // [17:22] is the sub-list for method output_type + 12, // [12:17] is the sub-list for method input_type + 12, // [12:12] is the sub-list for extension type_name + 12, // [12:12] is the sub-list for extension extendee + 0, // [0:12] is the sub-list for field type_name } func init() { file_officeconvertapi_v1_conversion_proto_init() } @@ -1053,13 +1228,16 @@ func file_officeconvertapi_v1_conversion_proto_init() { if File_officeconvertapi_v1_conversion_proto != nil { return } + file_officeconvertapi_v1_conversion_proto_msgTypes[1].OneofWrappers = []any{ + (*SlideRasterOptions_Jpeg)(nil), + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_officeconvertapi_v1_conversion_proto_rawDesc), len(file_officeconvertapi_v1_conversion_proto_rawDesc)), NumEnums: 3, - NumMessages: 12, + NumMessages: 14, NumExtensions: 0, NumServices: 1, }, diff --git a/gen/python/officeconvertapi/v1/conversion_pb2.py b/gen/python/officeconvertapi/v1/conversion_pb2.py index afe0050..acb3237 100644 --- a/gen/python/officeconvertapi/v1/conversion_pb2.py +++ b/gen/python/officeconvertapi/v1/conversion_pb2.py @@ -25,7 +25,7 @@ _sym_db = _symbol_database.Default() from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n$officeconvertapi/v1/conversion.proto\x12\x13officeconvertapi.v1\x1a\x1fgoogle/protobuf/timestamp.proto\"[\n\x05Slide\x12\x14\n\x05index\x18\x01 \x01(\x05R\x05index\x12\x1f\n\x0bnotes_plain\x18\x02 \x01(\tR\nnotesPlain\x12\x1b\n\timage_url\x18\x03 \x01(\tR\x08imageUrl\"\xf6\x01\n\tSlideDeck\x12#\n\rconversion_id\x18\x01 \x01(\tR\x0c\x63onversionId\x12\'\n\x0fsource_filename\x18\x02 \x01(\tR\x0esourceFilename\x12\x32\n\x06slides\x18\x03 \x03(\x0b\x32\x1a.officeconvertapi.v1.SlideR\x06slides\x12\x39\n\ncreated_at\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\tcreatedAt\x12\x14\n\x05width\x18\x05 \x01(\x05R\x05width\x12\x16\n\x06height\x18\x06 \x01(\x05R\x06height\"\x8d\x01\n\x17\x43reateConversionRequest\x12\'\n\x0fsource_filename\x18\x01 \x01(\tR\x0esourceFilename\x12I\n\nresolution\x18\x02 \x01(\x0e\x32).officeconvertapi.v1.ConversionResolutionR\nresolution\"\xea\x01\n\x18\x43reateConversionResponse\x12#\n\rconversion_id\x18\x01 \x01(\tR\x0c\x63onversionId\x12#\n\rupload_bucket\x18\x02 \x01(\tR\x0cuploadBucket\x12*\n\x11upload_object_key\x18\x03 \x01(\tR\x0fuploadObjectKey\x12\x1d\n\nupload_url\x18\x04 \x01(\tR\tuploadUrl\x12\x39\n\nexpires_at\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\texpiresAt\"=\n\x16StartConversionRequest\x12#\n\rconversion_id\x18\x01 \x01(\tR\x0c\x63onversionId\"}\n\x17StartConversionResponse\x12#\n\rconversion_id\x18\x01 \x01(\tR\x0c\x63onversionId\x12=\n\x06status\x18\x02 \x01(\x0e\x32%.officeconvertapi.v1.ConversionStatusR\x06status\"A\n\x1aGetConversionStatusRequest\x12#\n\rconversion_id\x18\x01 \x01(\tR\x0c\x63onversionId\"\xeb\x02\n\x1bGetConversionStatusResponse\x12#\n\rconversion_id\x18\x01 \x01(\tR\x0c\x63onversionId\x12=\n\x06status\x18\x02 \x01(\x0e\x32%.officeconvertapi.v1.ConversionStatusR\x06status\x12#\n\rerror_message\x18\x03 \x01(\tR\x0c\x65rrorMessage\x12\x39\n\nupdated_at\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\tupdatedAt\x12:\n\x05phase\x18\x05 \x01(\x0e\x32$.officeconvertapi.v1.ConversionPhaseR\x05phase\x12)\n\x10\x63urrent_progress\x18\x06 \x01(\x05R\x0f\x63urrentProgress\x12!\n\x0cmax_progress\x18\x07 \x01(\x05R\x0bmaxProgress\":\n\x13GetSlideDeckRequest\x12#\n\rconversion_id\x18\x01 \x01(\tR\x0c\x63onversionId\"U\n\x14GetSlideDeckResponse\x12=\n\nslide_deck\x18\x01 \x01(\x0b\x32\x1e.officeconvertapi.v1.SlideDeckR\tslideDeck\">\n\x17\x44\x65leteConversionRequest\x12#\n\rconversion_id\x18\x01 \x01(\tR\x0c\x63onversionId\"Y\n\x18\x44\x65leteConversionResponse\x12#\n\rconversion_id\x18\x01 \x01(\tR\x0c\x63onversionId\x12\x18\n\x07\x64\x65leted\x18\x02 \x01(\x08R\x07\x64\x65leted*\xb2\x01\n\x10\x43onversionStatus\x12!\n\x1d\x43ONVERSION_STATUS_UNSPECIFIED\x10\x00\x12\x1d\n\x19\x43ONVERSION_STATUS_PENDING\x10\x01\x12\x1d\n\x19\x43ONVERSION_STATUS_RUNNING\x10\x02\x12\x1f\n\x1b\x43ONVERSION_STATUS_SUCCEEDED\x10\x03\x12\x1c\n\x18\x43ONVERSION_STATUS_FAILED\x10\x04*\xe7\x01\n\x0f\x43onversionPhase\x12 \n\x1c\x43ONVERSION_PHASE_UNSPECIFIED\x10\x00\x12\x1d\n\x19\x43ONVERSION_PHASE_INACTIVE\x10\x01\x12%\n!CONVERSION_PHASE_EXTRACTING_NOTES\x10\x02\x12 \n\x1c\x43ONVERSION_PHASE_PPTX_TO_PDF\x10\x03\x12\"\n\x1e\x43ONVERSION_PHASE_PDF_TO_IMAGES\x10\x04\x12&\n\"CONVERSION_PHASE_UPLOADING_RESULTS\x10\x05*\xd6\x01\n\x14\x43onversionResolution\x12%\n!CONVERSION_RESOLUTION_UNSPECIFIED\x10\x00\x12\x1c\n\x18\x43ONVERSION_RESOLUTION_SD\x10\x01\x12\x1c\n\x18\x43ONVERSION_RESOLUTION_HD\x10\x02\x12\x1d\n\x19\x43ONVERSION_RESOLUTION_FHD\x10\x03\x12\x1d\n\x19\x43ONVERSION_RESOLUTION_QHD\x10\x04\x12\x1d\n\x19\x43ONVERSION_RESOLUTION_UHD\x10\x05\x32\xcc\x04\n\x11\x43onversionService\x12q\n\x10\x43reateConversion\x12,.officeconvertapi.v1.CreateConversionRequest\x1a-.officeconvertapi.v1.CreateConversionResponse\"\x00\x12n\n\x0fStartConversion\x12+.officeconvertapi.v1.StartConversionRequest\x1a,.officeconvertapi.v1.StartConversionResponse\"\x00\x12z\n\x13GetConversionStatus\x12/.officeconvertapi.v1.GetConversionStatusRequest\x1a\x30.officeconvertapi.v1.GetConversionStatusResponse\"\x00\x12\x65\n\x0cGetSlideDeck\x12(.officeconvertapi.v1.GetSlideDeckRequest\x1a).officeconvertapi.v1.GetSlideDeckResponse\"\x00\x12q\n\x10\x44\x65leteConversion\x12,.officeconvertapi.v1.DeleteConversionRequest\x1a-.officeconvertapi.v1.DeleteConversionResponse\"\x00\x42ZZXgitea.auvem.com/end-internal/officeconvert/gen/go/officeconvertapi/v1;officeconvertapiv1b\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n$officeconvertapi/v1/conversion.proto\x12\x13officeconvertapi.v1\x1a\x1fgoogle/protobuf/timestamp.proto\"-\n\x11JpegOutputOptions\x12\x18\n\x07quality\x18\x01 \x01(\x05R\x07quality\"\xa7\x01\n\x12SlideRasterOptions\x12I\n\nresolution\x18\x01 \x01(\x0e\x32).officeconvertapi.v1.ConversionResolutionR\nresolution\x12<\n\x04jpeg\x18\x02 \x01(\x0b\x32&.officeconvertapi.v1.JpegOutputOptionsH\x00R\x04jpegB\x08\n\x06\x66ormat\"\x8b\x01\n\x05Slide\x12\x14\n\x05index\x18\x01 \x01(\x05R\x05index\x12\x1f\n\x0bnotes_plain\x18\x02 \x01(\tR\nnotesPlain\x12\x1b\n\timage_url\x18\x03 \x01(\tR\x08imageUrl\x12.\n\x13thumbnail_image_url\x18\x04 \x01(\tR\x11thumbnailImageUrl\"\xca\x02\n\tSlideDeck\x12#\n\rconversion_id\x18\x01 \x01(\tR\x0c\x63onversionId\x12\'\n\x0fsource_filename\x18\x02 \x01(\tR\x0esourceFilename\x12\x32\n\x06slides\x18\x03 \x03(\x0b\x32\x1a.officeconvertapi.v1.SlideR\x06slides\x12\x39\n\ncreated_at\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\tcreatedAt\x12\x14\n\x05width\x18\x05 \x01(\x05R\x05width\x12\x16\n\x06height\x18\x06 \x01(\x05R\x06height\x12\'\n\x0fthumbnail_width\x18\x07 \x01(\x05R\x0ethumbnailWidth\x12)\n\x10thumbnail_height\x18\x08 \x01(\x05R\x0fthumbnailHeight\"\xc6\x01\n\x17\x43reateConversionRequest\x12\'\n\x0fsource_filename\x18\x01 \x01(\tR\x0esourceFilename\x12;\n\x04\x66ull\x18\x02 \x01(\x0b\x32\'.officeconvertapi.v1.SlideRasterOptionsR\x04\x66ull\x12\x45\n\tthumbnail\x18\x03 \x01(\x0b\x32\'.officeconvertapi.v1.SlideRasterOptionsR\tthumbnail\"\xea\x01\n\x18\x43reateConversionResponse\x12#\n\rconversion_id\x18\x01 \x01(\tR\x0c\x63onversionId\x12#\n\rupload_bucket\x18\x02 \x01(\tR\x0cuploadBucket\x12*\n\x11upload_object_key\x18\x03 \x01(\tR\x0fuploadObjectKey\x12\x1d\n\nupload_url\x18\x04 \x01(\tR\tuploadUrl\x12\x39\n\nexpires_at\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\texpiresAt\"=\n\x16StartConversionRequest\x12#\n\rconversion_id\x18\x01 \x01(\tR\x0c\x63onversionId\"}\n\x17StartConversionResponse\x12#\n\rconversion_id\x18\x01 \x01(\tR\x0c\x63onversionId\x12=\n\x06status\x18\x02 \x01(\x0e\x32%.officeconvertapi.v1.ConversionStatusR\x06status\"A\n\x1aGetConversionStatusRequest\x12#\n\rconversion_id\x18\x01 \x01(\tR\x0c\x63onversionId\"\xeb\x02\n\x1bGetConversionStatusResponse\x12#\n\rconversion_id\x18\x01 \x01(\tR\x0c\x63onversionId\x12=\n\x06status\x18\x02 \x01(\x0e\x32%.officeconvertapi.v1.ConversionStatusR\x06status\x12#\n\rerror_message\x18\x03 \x01(\tR\x0c\x65rrorMessage\x12\x39\n\nupdated_at\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\tupdatedAt\x12:\n\x05phase\x18\x05 \x01(\x0e\x32$.officeconvertapi.v1.ConversionPhaseR\x05phase\x12)\n\x10\x63urrent_progress\x18\x06 \x01(\x05R\x0f\x63urrentProgress\x12!\n\x0cmax_progress\x18\x07 \x01(\x05R\x0bmaxProgress\":\n\x13GetSlideDeckRequest\x12#\n\rconversion_id\x18\x01 \x01(\tR\x0c\x63onversionId\"U\n\x14GetSlideDeckResponse\x12=\n\nslide_deck\x18\x01 \x01(\x0b\x32\x1e.officeconvertapi.v1.SlideDeckR\tslideDeck\">\n\x17\x44\x65leteConversionRequest\x12#\n\rconversion_id\x18\x01 \x01(\tR\x0c\x63onversionId\"Y\n\x18\x44\x65leteConversionResponse\x12#\n\rconversion_id\x18\x01 \x01(\tR\x0c\x63onversionId\x12\x18\n\x07\x64\x65leted\x18\x02 \x01(\x08R\x07\x64\x65leted*\xb2\x01\n\x10\x43onversionStatus\x12!\n\x1d\x43ONVERSION_STATUS_UNSPECIFIED\x10\x00\x12\x1d\n\x19\x43ONVERSION_STATUS_PENDING\x10\x01\x12\x1d\n\x19\x43ONVERSION_STATUS_RUNNING\x10\x02\x12\x1f\n\x1b\x43ONVERSION_STATUS_SUCCEEDED\x10\x03\x12\x1c\n\x18\x43ONVERSION_STATUS_FAILED\x10\x04*\xe7\x01\n\x0f\x43onversionPhase\x12 \n\x1c\x43ONVERSION_PHASE_UNSPECIFIED\x10\x00\x12\x1d\n\x19\x43ONVERSION_PHASE_INACTIVE\x10\x01\x12%\n!CONVERSION_PHASE_EXTRACTING_NOTES\x10\x02\x12 \n\x1c\x43ONVERSION_PHASE_PPTX_TO_PDF\x10\x03\x12\"\n\x1e\x43ONVERSION_PHASE_PDF_TO_IMAGES\x10\x04\x12&\n\"CONVERSION_PHASE_UPLOADING_RESULTS\x10\x05*\xd6\x01\n\x14\x43onversionResolution\x12%\n!CONVERSION_RESOLUTION_UNSPECIFIED\x10\x00\x12\x1c\n\x18\x43ONVERSION_RESOLUTION_SD\x10\x01\x12\x1c\n\x18\x43ONVERSION_RESOLUTION_HD\x10\x02\x12\x1d\n\x19\x43ONVERSION_RESOLUTION_FHD\x10\x03\x12\x1d\n\x19\x43ONVERSION_RESOLUTION_QHD\x10\x04\x12\x1d\n\x19\x43ONVERSION_RESOLUTION_UHD\x10\x05\x32\xcc\x04\n\x11\x43onversionService\x12q\n\x10\x43reateConversion\x12,.officeconvertapi.v1.CreateConversionRequest\x1a-.officeconvertapi.v1.CreateConversionResponse\"\x00\x12n\n\x0fStartConversion\x12+.officeconvertapi.v1.StartConversionRequest\x1a,.officeconvertapi.v1.StartConversionResponse\"\x00\x12z\n\x13GetConversionStatus\x12/.officeconvertapi.v1.GetConversionStatusRequest\x1a\x30.officeconvertapi.v1.GetConversionStatusResponse\"\x00\x12\x65\n\x0cGetSlideDeck\x12(.officeconvertapi.v1.GetSlideDeckRequest\x1a).officeconvertapi.v1.GetSlideDeckResponse\"\x00\x12q\n\x10\x44\x65leteConversion\x12,.officeconvertapi.v1.DeleteConversionRequest\x1a-.officeconvertapi.v1.DeleteConversionResponse\"\x00\x42ZZXgitea.auvem.com/end-internal/officeconvert/gen/go/officeconvertapi/v1;officeconvertapiv1b\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -33,36 +33,40 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'officeconvertapi.v1.convers if not _descriptor._USE_C_DESCRIPTORS: _globals['DESCRIPTOR']._loaded_options = None _globals['DESCRIPTOR']._serialized_options = b'ZXgitea.auvem.com/end-internal/officeconvert/gen/go/officeconvertapi/v1;officeconvertapiv1' - _globals['_CONVERSIONSTATUS']._serialized_start=1743 - _globals['_CONVERSIONSTATUS']._serialized_end=1921 - _globals['_CONVERSIONPHASE']._serialized_start=1924 - _globals['_CONVERSIONPHASE']._serialized_end=2155 - _globals['_CONVERSIONRESOLUTION']._serialized_start=2158 - _globals['_CONVERSIONRESOLUTION']._serialized_end=2372 - _globals['_SLIDE']._serialized_start=94 - _globals['_SLIDE']._serialized_end=185 - _globals['_SLIDEDECK']._serialized_start=188 - _globals['_SLIDEDECK']._serialized_end=434 - _globals['_CREATECONVERSIONREQUEST']._serialized_start=437 - _globals['_CREATECONVERSIONREQUEST']._serialized_end=578 - _globals['_CREATECONVERSIONRESPONSE']._serialized_start=581 - _globals['_CREATECONVERSIONRESPONSE']._serialized_end=815 - _globals['_STARTCONVERSIONREQUEST']._serialized_start=817 - _globals['_STARTCONVERSIONREQUEST']._serialized_end=878 - _globals['_STARTCONVERSIONRESPONSE']._serialized_start=880 - _globals['_STARTCONVERSIONRESPONSE']._serialized_end=1005 - _globals['_GETCONVERSIONSTATUSREQUEST']._serialized_start=1007 - _globals['_GETCONVERSIONSTATUSREQUEST']._serialized_end=1072 - _globals['_GETCONVERSIONSTATUSRESPONSE']._serialized_start=1075 - _globals['_GETCONVERSIONSTATUSRESPONSE']._serialized_end=1438 - _globals['_GETSLIDEDECKREQUEST']._serialized_start=1440 - _globals['_GETSLIDEDECKREQUEST']._serialized_end=1498 - _globals['_GETSLIDEDECKRESPONSE']._serialized_start=1500 - _globals['_GETSLIDEDECKRESPONSE']._serialized_end=1585 - _globals['_DELETECONVERSIONREQUEST']._serialized_start=1587 - _globals['_DELETECONVERSIONREQUEST']._serialized_end=1649 - _globals['_DELETECONVERSIONRESPONSE']._serialized_start=1651 - _globals['_DELETECONVERSIONRESPONSE']._serialized_end=1740 - _globals['_CONVERSIONSERVICE']._serialized_start=2375 - _globals['_CONVERSIONSERVICE']._serialized_end=2963 + _globals['_CONVERSIONSTATUS']._serialized_start=2150 + _globals['_CONVERSIONSTATUS']._serialized_end=2328 + _globals['_CONVERSIONPHASE']._serialized_start=2331 + _globals['_CONVERSIONPHASE']._serialized_end=2562 + _globals['_CONVERSIONRESOLUTION']._serialized_start=2565 + _globals['_CONVERSIONRESOLUTION']._serialized_end=2779 + _globals['_JPEGOUTPUTOPTIONS']._serialized_start=94 + _globals['_JPEGOUTPUTOPTIONS']._serialized_end=139 + _globals['_SLIDERASTEROPTIONS']._serialized_start=142 + _globals['_SLIDERASTEROPTIONS']._serialized_end=309 + _globals['_SLIDE']._serialized_start=312 + _globals['_SLIDE']._serialized_end=451 + _globals['_SLIDEDECK']._serialized_start=454 + _globals['_SLIDEDECK']._serialized_end=784 + _globals['_CREATECONVERSIONREQUEST']._serialized_start=787 + _globals['_CREATECONVERSIONREQUEST']._serialized_end=985 + _globals['_CREATECONVERSIONRESPONSE']._serialized_start=988 + _globals['_CREATECONVERSIONRESPONSE']._serialized_end=1222 + _globals['_STARTCONVERSIONREQUEST']._serialized_start=1224 + _globals['_STARTCONVERSIONREQUEST']._serialized_end=1285 + _globals['_STARTCONVERSIONRESPONSE']._serialized_start=1287 + _globals['_STARTCONVERSIONRESPONSE']._serialized_end=1412 + _globals['_GETCONVERSIONSTATUSREQUEST']._serialized_start=1414 + _globals['_GETCONVERSIONSTATUSREQUEST']._serialized_end=1479 + _globals['_GETCONVERSIONSTATUSRESPONSE']._serialized_start=1482 + _globals['_GETCONVERSIONSTATUSRESPONSE']._serialized_end=1845 + _globals['_GETSLIDEDECKREQUEST']._serialized_start=1847 + _globals['_GETSLIDEDECKREQUEST']._serialized_end=1905 + _globals['_GETSLIDEDECKRESPONSE']._serialized_start=1907 + _globals['_GETSLIDEDECKRESPONSE']._serialized_end=1992 + _globals['_DELETECONVERSIONREQUEST']._serialized_start=1994 + _globals['_DELETECONVERSIONREQUEST']._serialized_end=2056 + _globals['_DELETECONVERSIONRESPONSE']._serialized_start=2058 + _globals['_DELETECONVERSIONRESPONSE']._serialized_end=2147 + _globals['_CONVERSIONSERVICE']._serialized_start=2782 + _globals['_CONVERSIONSERVICE']._serialized_end=3370 # @@protoc_insertion_point(module_scope) diff --git a/gen/python/officeconvertapi/v1/conversion_pb2.pyi b/gen/python/officeconvertapi/v1/conversion_pb2.pyi index fc5f083..645c468 100644 --- a/gen/python/officeconvertapi/v1/conversion_pb2.pyi +++ b/gen/python/officeconvertapi/v1/conversion_pb2.pyi @@ -53,39 +53,61 @@ CONVERSION_RESOLUTION_FHD: ConversionResolution CONVERSION_RESOLUTION_QHD: ConversionResolution CONVERSION_RESOLUTION_UHD: ConversionResolution +class JpegOutputOptions(_message.Message): + __slots__ = ("quality",) + QUALITY_FIELD_NUMBER: _ClassVar[int] + quality: int + def __init__(self, quality: _Optional[int] = ...) -> None: ... + +class SlideRasterOptions(_message.Message): + __slots__ = ("resolution", "jpeg") + RESOLUTION_FIELD_NUMBER: _ClassVar[int] + JPEG_FIELD_NUMBER: _ClassVar[int] + resolution: ConversionResolution + jpeg: JpegOutputOptions + def __init__(self, resolution: _Optional[_Union[ConversionResolution, str]] = ..., jpeg: _Optional[_Union[JpegOutputOptions, _Mapping]] = ...) -> None: ... + class Slide(_message.Message): - __slots__ = ("index", "notes_plain", "image_url") + __slots__ = ("index", "notes_plain", "image_url", "thumbnail_image_url") INDEX_FIELD_NUMBER: _ClassVar[int] NOTES_PLAIN_FIELD_NUMBER: _ClassVar[int] IMAGE_URL_FIELD_NUMBER: _ClassVar[int] + THUMBNAIL_IMAGE_URL_FIELD_NUMBER: _ClassVar[int] index: int notes_plain: str image_url: str - def __init__(self, index: _Optional[int] = ..., notes_plain: _Optional[str] = ..., image_url: _Optional[str] = ...) -> None: ... + thumbnail_image_url: str + def __init__(self, index: _Optional[int] = ..., notes_plain: _Optional[str] = ..., image_url: _Optional[str] = ..., thumbnail_image_url: _Optional[str] = ...) -> None: ... class SlideDeck(_message.Message): - __slots__ = ("conversion_id", "source_filename", "slides", "created_at", "width", "height") + __slots__ = ("conversion_id", "source_filename", "slides", "created_at", "width", "height", "thumbnail_width", "thumbnail_height") CONVERSION_ID_FIELD_NUMBER: _ClassVar[int] SOURCE_FILENAME_FIELD_NUMBER: _ClassVar[int] SLIDES_FIELD_NUMBER: _ClassVar[int] CREATED_AT_FIELD_NUMBER: _ClassVar[int] WIDTH_FIELD_NUMBER: _ClassVar[int] HEIGHT_FIELD_NUMBER: _ClassVar[int] + THUMBNAIL_WIDTH_FIELD_NUMBER: _ClassVar[int] + THUMBNAIL_HEIGHT_FIELD_NUMBER: _ClassVar[int] conversion_id: str source_filename: str slides: _containers.RepeatedCompositeFieldContainer[Slide] created_at: _timestamp_pb2.Timestamp width: int height: int - def __init__(self, conversion_id: _Optional[str] = ..., source_filename: _Optional[str] = ..., slides: _Optional[_Iterable[_Union[Slide, _Mapping]]] = ..., created_at: _Optional[_Union[datetime.datetime, _timestamp_pb2.Timestamp, _Mapping]] = ..., width: _Optional[int] = ..., height: _Optional[int] = ...) -> None: ... + thumbnail_width: int + thumbnail_height: int + def __init__(self, conversion_id: _Optional[str] = ..., source_filename: _Optional[str] = ..., slides: _Optional[_Iterable[_Union[Slide, _Mapping]]] = ..., created_at: _Optional[_Union[datetime.datetime, _timestamp_pb2.Timestamp, _Mapping]] = ..., width: _Optional[int] = ..., height: _Optional[int] = ..., thumbnail_width: _Optional[int] = ..., thumbnail_height: _Optional[int] = ...) -> None: ... class CreateConversionRequest(_message.Message): - __slots__ = ("source_filename", "resolution") + __slots__ = ("source_filename", "full", "thumbnail") SOURCE_FILENAME_FIELD_NUMBER: _ClassVar[int] - RESOLUTION_FIELD_NUMBER: _ClassVar[int] + FULL_FIELD_NUMBER: _ClassVar[int] + THUMBNAIL_FIELD_NUMBER: _ClassVar[int] source_filename: str - resolution: ConversionResolution - def __init__(self, source_filename: _Optional[str] = ..., resolution: _Optional[_Union[ConversionResolution, str]] = ...) -> None: ... + full: SlideRasterOptions + thumbnail: SlideRasterOptions + def __init__(self, source_filename: _Optional[str] = ..., full: _Optional[_Union[SlideRasterOptions, _Mapping]] = ..., thumbnail: _Optional[_Union[SlideRasterOptions, _Mapping]] = ...) -> None: ... class CreateConversionResponse(_message.Message): __slots__ = ("conversion_id", "upload_bucket", "upload_object_key", "upload_url", "expires_at") diff --git a/proto/officeconvertapi/v1/conversion.proto b/proto/officeconvertapi/v1/conversion.proto index d35234b..717fbe3 100644 --- a/proto/officeconvertapi/v1/conversion.proto +++ b/proto/officeconvertapi/v1/conversion.proto @@ -53,11 +53,29 @@ enum ConversionResolution { CONVERSION_RESOLUTION_UHD = 5; } +// JpegOutputOptions configures JPEG-specific encoding controls. +message JpegOutputOptions { + // JPEG quality from 1..100. 0 means server default. + int32 quality = 1; +} + +// SlideRasterOptions defines rendering settings for a raster output tier. +message SlideRasterOptions { + // Output resolution preset. UNSPECIFIED means server default for the tier. + ConversionResolution resolution = 1; + oneof format { + JpegOutputOptions jpeg = 2; + } +} + // Slide contains extracted notes and the rendered image URL for one slide. message Slide { int32 index = 1; string notes_plain = 2; + // Full-size rendered image URL. string image_url = 3; + // Thumbnail rendered image URL. + string thumbnail_image_url = 4; } // SlideDeck is the final structured conversion artifact. @@ -67,14 +85,21 @@ message SlideDeck { string source_filename = 2; repeated Slide slides = 3; google.protobuf.Timestamp created_at = 4; + // Full-size raster width in pixels. int32 width = 5; + // Full-size raster height in pixels. int32 height = 6; + // Thumbnail raster width in pixels. + int32 thumbnail_width = 7; + // Thumbnail raster height in pixels. + int32 thumbnail_height = 8; } // CreateConversionRequest starts a conversion session. message CreateConversionRequest { string source_filename = 1; - ConversionResolution resolution = 2; + SlideRasterOptions full = 2; + SlideRasterOptions thumbnail = 3; } // CreateConversionResponse returns upload details for the session. diff --git a/python/packages/officeconvert/src/officeconvert/conversion.py b/python/packages/officeconvert/src/officeconvert/conversion.py index 65f609d..effcb60 100644 --- a/python/packages/officeconvert/src/officeconvert/conversion.py +++ b/python/packages/officeconvert/src/officeconvert/conversion.py @@ -19,6 +19,7 @@ class SlideArtifact: index: int image_path: Path + thumbnail_path: Path notes_plain: str @@ -30,6 +31,8 @@ class SlideDeckResult: slides: list[SlideArtifact] width: int height: int + thumbnail_width: int + thumbnail_height: int inferred_dpi: int pptx_to_pdf_timeout_s: int pdf_to_images_timeout_s: int @@ -133,6 +136,7 @@ def render_pdf_to_images( target_width: int | None = None, target_height: int | None = None, image_format: str = "png", + jpeg_quality: int | None = None, timeout_s: int = 120, total_pages: int | None = None, operation_timeout_s: int | None = None, @@ -162,6 +166,15 @@ def render_pdf_to_images( raise ValueError("target_width must be greater than zero") if target_height is not None and target_height <= 0: raise ValueError("target_height must be greater than zero") + normalized_image_format = _normalize_image_format(image_format) + output_suffix = _image_output_suffix(normalized_image_format) + normalized_jpeg_quality = _normalize_jpeg_quality(jpeg_quality) + jpeg_options_args: list[str] = [] + if normalized_jpeg_quality is not None and normalized_image_format in { + "jpeg", + "jpegcmyk", + }: + jpeg_options_args = ["-jpegopt", f"quality={normalized_jpeg_quality}"] out_dir.mkdir(parents=True, exist_ok=True) scale_args: list[str] = [] @@ -174,7 +187,8 @@ def render_pdf_to_images( "-r", str(dpi), *scale_args, - f"-{image_format}", + *jpeg_options_args, + f"-{normalized_image_format}", str(pdf_path.resolve()), str(prefix_path), ] @@ -198,7 +212,7 @@ def render_pdf_to_images( raise RuntimeError( f"Poppler rasterization failed: {completed.stderr.strip() or completed.stdout.strip()}" ) - images = sorted(out_dir.glob(f"slide-*.{image_format}")) + images = sorted(out_dir.glob(f"slide-*.{output_suffix}")) else: if total_pages < 0: raise ValueError("total_pages must be zero or greater") @@ -210,7 +224,8 @@ def render_pdf_to_images( "-r", str(dpi), *scale_args, - f"-{image_format}", + *jpeg_options_args, + f"-{normalized_image_format}", "-f", str(page_index), "-l", @@ -246,7 +261,7 @@ def render_pdf_to_images( "Poppler rasterization failed on page " f"{page_index}: {completed.stderr.strip() or completed.stdout.strip()}" ) - image_path = page_prefix.with_suffix(f".{image_format}") + image_path = page_prefix.with_suffix(f".{output_suffix}") if not image_path.exists(): raise RuntimeError( f"Poppler did not create expected page image: {image_path}" @@ -289,8 +304,10 @@ def convert_pptx_to_slidedeck( pptx_path: Path, work_dir: Path, *, - resolution: str = RESOLUTION_FHD, - image_format: str = "png", + full_resolution: str = RESOLUTION_FHD, + thumbnail_resolution: str = RESOLUTION_SD, + full_jpeg_quality: int | None = None, + thumbnail_jpeg_quality: int | None = None, pptx_to_pdf_timeout_s: int = 180, pdf_to_images_timeout_s: int = 1800, pptx_to_pdf_base_timeout_s: int = 45, @@ -308,8 +325,10 @@ def convert_pptx_to_slidedeck( Args: pptx_path: Source `.pptx` path. work_dir: Scratch directory for generated outputs. - resolution: Output resolution preset (`sd`, `hd`, `fhd`, `qhd`, `uhd`). - image_format: Output image format accepted by `pdftoppm`. + full_resolution: Full-size output resolution preset (`sd`, `hd`, `fhd`, `qhd`, `uhd`). + thumbnail_resolution: Thumbnail output resolution preset (`sd`, `hd`, `fhd`, `qhd`, `uhd`). + full_jpeg_quality: Full-size JPEG quality (`1..100`); `None`/`0` means default. + thumbnail_jpeg_quality: Thumbnail JPEG quality (`1..100`); `None`/`0` means default. pptx_to_pdf_timeout_s: Timeout in seconds for the LibreOffice subprocess. pdf_to_images_timeout_s: Timeout in seconds for the Poppler subprocess. @@ -322,7 +341,8 @@ def convert_pptx_to_slidedeck( work_dir = work_dir.resolve() work_dir.mkdir(parents=True, exist_ok=True) pdf_path = work_dir / f"{pptx_path.stem}.pdf" - image_dir = work_dir / "slides" + full_image_dir = work_dir / "slides_full" + thumbnail_image_dir = work_dir / "slides_thumb" _emit_progress(progress_callback, PHASE_EXTRACTING_NOTES, 0, 1) notes = extract_slide_notes(pptx_path) @@ -331,7 +351,12 @@ def convert_pptx_to_slidedeck( output_width, output_height = _infer_output_dimensions_from_slide_size( slide_width=slide_width, slide_height=slide_height, - resolution=resolution, + resolution=full_resolution, + ) + thumbnail_width, thumbnail_height = _infer_output_dimensions_from_slide_size( + slide_width=slide_width, + slide_height=slide_height, + resolution=thumbnail_resolution, ) inferred_dpi = infer_minimum_raster_dpi( slide_width_emu=slide_width, @@ -339,6 +364,12 @@ def convert_pptx_to_slidedeck( output_width_px=output_width, output_height_px=output_height, ) + thumbnail_inferred_dpi = infer_minimum_raster_dpi( + slide_width_emu=slide_width, + slide_height_emu=slide_height, + output_width_px=thumbnail_width, + output_height_px=thumbnail_height, + ) slide_count = len(notes) pptx_to_pdf_timeout = _compute_adaptive_timeout( slide_count=slide_count, @@ -347,7 +378,7 @@ def convert_pptx_to_slidedeck( per_slide_timeout_s=pptx_to_pdf_per_slide_timeout_s, ) pdf_to_images_timeout = _compute_adaptive_timeout( - slide_count=slide_count, + slide_count=slide_count * 2, timeout_cap_s=pdf_to_images_timeout_s, base_timeout_s=pdf_to_images_base_timeout_s, per_slide_timeout_s=pdf_to_images_per_slide_timeout_s, @@ -357,34 +388,43 @@ def convert_pptx_to_slidedeck( convert_pptx_to_pdf(pptx_path, pdf_path, timeout_s=pptx_to_pdf_timeout) _emit_progress(progress_callback, PHASE_PPTX_TO_PDF, 1, 1) - _emit_progress(progress_callback, PHASE_PDF_TO_IMAGES, 0, slide_count) + raster_steps = slide_count * 2 + _emit_progress(progress_callback, PHASE_PDF_TO_IMAGES, 0, raster_steps) pdf_to_images_page_timeout = _compute_page_timeout( total_timeout_s=pdf_to_images_timeout, - page_count=slide_count, + page_count=raster_steps, base_timeout_s=pdf_to_images_base_timeout_s, ) logger.info( - "Conversion plan source=%s slides=%d inferred_dpi=%d image_format=%s " - "resolution=%s output_size=%dx%d " + "Conversion plan source=%s slides=%d image_format=%s " + "full[resolution=%s size=%dx%d dpi=%d jpeg_quality=%s] " + "thumbnail[resolution=%s size=%dx%d dpi=%d jpeg_quality=%s] " "computed_timeouts_s[pptx_to_pdf_total=%d,pdf_to_images_total=%d,pdf_to_images_per_page=%d]", pptx_path.name, slide_count, - inferred_dpi, - image_format, - resolution, + "jpeg", + full_resolution, output_width, output_height, + inferred_dpi, + str(full_jpeg_quality if full_jpeg_quality is not None else "default"), + thumbnail_resolution, + thumbnail_width, + thumbnail_height, + thumbnail_inferred_dpi, + str(thumbnail_jpeg_quality if thumbnail_jpeg_quality is not None else "default"), pptx_to_pdf_timeout, pdf_to_images_timeout, pdf_to_images_page_timeout, ) - image_paths = render_pdf_to_images( + full_image_paths = render_pdf_to_images( pdf_path, - image_dir, + full_image_dir, dpi=inferred_dpi, target_width=output_width, target_height=output_height, - image_format=image_format, + image_format="jpeg", + jpeg_quality=full_jpeg_quality, timeout_s=pdf_to_images_page_timeout, total_pages=slide_count, operation_timeout_s=pdf_to_images_timeout, @@ -395,22 +435,55 @@ def convert_pptx_to_slidedeck( max_pages, ), ) + thumbnail_image_paths = render_pdf_to_images( + pdf_path, + thumbnail_image_dir, + dpi=thumbnail_inferred_dpi, + target_width=thumbnail_width, + target_height=thumbnail_height, + image_format="jpeg", + jpeg_quality=thumbnail_jpeg_quality, + timeout_s=pdf_to_images_page_timeout, + total_pages=slide_count, + operation_timeout_s=pdf_to_images_timeout, + page_progress_callback=lambda current, max_pages: _emit_progress( + progress_callback, + PHASE_PDF_TO_IMAGES, + slide_count + current, + slide_count + max_pages, + ), + ) - if len(image_paths) != len(notes): + if len(full_image_paths) != len(notes): raise ValueError( - "rendered slide count does not match note count: " - f"{len(image_paths)} image(s) vs {len(notes)} note entries" + "rendered full-size slide count does not match note count: " + f"{len(full_image_paths)} image(s) vs {len(notes)} note entries" + ) + if len(thumbnail_image_paths) != len(notes): + raise ValueError( + "rendered thumbnail slide count does not match note count: " + f"{len(thumbnail_image_paths)} image(s) vs {len(notes)} note entries" ) slides = [ - SlideArtifact(index=index, image_path=image_path, notes_plain=note) - for index, (image_path, note) in enumerate(zip(image_paths, notes), start=1) + SlideArtifact( + index=index, + image_path=image_path, + thumbnail_path=thumbnail_path, + notes_plain=note, + ) + for index, (image_path, thumbnail_path, note) in enumerate( + zip(full_image_paths, thumbnail_image_paths, notes), + start=1, + ) ] return SlideDeckResult( source_filename=pptx_path.name, slides=slides, width=output_width, height=output_height, + thumbnail_width=thumbnail_width, + thumbnail_height=thumbnail_height, inferred_dpi=inferred_dpi, pptx_to_pdf_timeout_s=pptx_to_pdf_timeout, pdf_to_images_timeout_s=pdf_to_images_timeout, @@ -511,6 +584,30 @@ def _compute_page_timeout( return max(base_timeout_s, timeout) +def _normalize_image_format(image_format: str) -> str: + """Normalize image format aliases accepted by Poppler.""" + normalized = image_format.strip().lower() + if normalized == "jpg": + return "jpeg" + return normalized + + +def _image_output_suffix(image_format: str) -> str: + """Return output filename suffix for a normalized Poppler image format.""" + if image_format in {"jpeg", "jpegcmyk"}: + return "jpg" + return image_format + + +def _normalize_jpeg_quality(jpeg_quality: int | None) -> int | None: + """Normalize optional JPEG quality; 0 means server/default quality.""" + if jpeg_quality is None or jpeg_quality == 0: + return None + if 1 <= jpeg_quality <= 100: + return jpeg_quality + raise ValueError("jpeg_quality must be 0 or between 1 and 100") + + def _emit_progress( progress_callback: ProgressCallback | None, phase: str, diff --git a/python/packages/server/src/officeconvert_server/models.py b/python/packages/server/src/officeconvert_server/models.py index 6b1d6e2..fa94f83 100644 --- a/python/packages/server/src/officeconvert_server/models.py +++ b/python/packages/server/src/officeconvert_server/models.py @@ -19,7 +19,10 @@ class ConversionSession: conversion_id: str source_filename: str - resolution: conversion_pb2.ConversionResolution + full_resolution: conversion_pb2.ConversionResolution + thumbnail_resolution: conversion_pb2.ConversionResolution + full_jpeg_quality: int + thumbnail_jpeg_quality: int bucket_name: str upload_object_key: str status: conversion_pb2.ConversionStatus diff --git a/python/packages/server/src/officeconvert_server/service.py b/python/packages/server/src/officeconvert_server/service.py index 8093446..ceb8170 100644 --- a/python/packages/server/src/officeconvert_server/service.py +++ b/python/packages/server/src/officeconvert_server/service.py @@ -45,6 +45,10 @@ _RESOLUTION_PRESET_BY_PROTO = { conversion_pb2.CONVERSION_RESOLUTION_QHD: RESOLUTION_QHD, conversion_pb2.CONVERSION_RESOLUTION_UHD: RESOLUTION_UHD, } +_DEFAULT_FULL_RESOLUTION = conversion_pb2.CONVERSION_RESOLUTION_FHD +_DEFAULT_THUMBNAIL_RESOLUTION = conversion_pb2.CONVERSION_RESOLUTION_SD +_DEFAULT_FULL_JPEG_QUALITY = 85 +_DEFAULT_THUMBNAIL_JPEG_QUALITY = 75 class ConversionServiceImpl(conversion_connect.ConversionService): @@ -69,11 +73,18 @@ class ConversionServiceImpl(conversion_connect.ConversionService): raise ConnectError(Code.INVALID_ARGUMENT, "source_filename is required") if not source_filename.lower().endswith(".pptx"): raise ConnectError(Code.INVALID_ARGUMENT, "only .pptx input is supported") - resolution = request.resolution - if resolution == conversion_pb2.CONVERSION_RESOLUTION_UNSPECIFIED: - resolution = conversion_pb2.CONVERSION_RESOLUTION_FHD - if resolution not in _RESOLUTION_PRESET_BY_PROTO: - raise ConnectError(Code.INVALID_ARGUMENT, "resolution is invalid") + full_resolution, full_jpeg_quality = self._resolve_raster_options( + request.full, + default_resolution=_DEFAULT_FULL_RESOLUTION, + default_jpeg_quality=_DEFAULT_FULL_JPEG_QUALITY, + field_name="full", + ) + thumbnail_resolution, thumbnail_jpeg_quality = self._resolve_raster_options( + request.thumbnail, + default_resolution=_DEFAULT_THUMBNAIL_RESOLUTION, + default_jpeg_quality=_DEFAULT_THUMBNAIL_JPEG_QUALITY, + field_name="thumbnail", + ) ksuid = Ksuid() conversion_id = str(ksuid) @@ -109,7 +120,10 @@ class ConversionServiceImpl(conversion_connect.ConversionService): session = ConversionSession( conversion_id=conversion_id, source_filename=source_filename, - resolution=resolution, + full_resolution=full_resolution, + thumbnail_resolution=thumbnail_resolution, + full_jpeg_quality=full_jpeg_quality, + thumbnail_jpeg_quality=thumbnail_jpeg_quality, bucket_name=bucket_name, upload_object_key=upload_key, status=conversion_pb2.CONVERSION_STATUS_PENDING, @@ -226,11 +240,15 @@ class ConversionServiceImpl(conversion_connect.ConversionService): """Execute conversion flow and persist terminal state in memory.""" started_at = time.monotonic() logger.info( - "Starting conversion conversion_id=%s source_filename=%s resolution=%s " + "Starting conversion conversion_id=%s source_filename=%s " + "full[resolution=%s,jpeg_quality=%d] thumbnail[resolution=%s,jpeg_quality=%d] " "timeout_caps_s[pptx_to_pdf_total=%d,pdf_to_images_total=%d]", session.conversion_id, session.source_filename, - conversion_pb2.ConversionResolution.Name(session.resolution), + conversion_pb2.ConversionResolution.Name(session.full_resolution), + session.full_jpeg_quality, + conversion_pb2.ConversionResolution.Name(session.thumbnail_resolution), + session.thumbnail_jpeg_quality, self._config.conversion_pptx_to_pdf_timeout_seconds, self._config.conversion_pdf_to_images_timeout_seconds, ) @@ -250,7 +268,12 @@ class ConversionServiceImpl(conversion_connect.ConversionService): convert_pptx_to_slidedeck, source_path, work_dir, - resolution=_RESOLUTION_PRESET_BY_PROTO[session.resolution], + full_resolution=_RESOLUTION_PRESET_BY_PROTO[session.full_resolution], + thumbnail_resolution=_RESOLUTION_PRESET_BY_PROTO[ + session.thumbnail_resolution + ], + full_jpeg_quality=session.full_jpeg_quality, + thumbnail_jpeg_quality=session.thumbnail_jpeg_quality, pptx_to_pdf_timeout_s=self._config.conversion_pptx_to_pdf_timeout_seconds, pdf_to_images_timeout_s=self._config.conversion_pdf_to_images_timeout_seconds, pptx_to_pdf_base_timeout_s=self._config.conversion_pptx_to_pdf_base_timeout_seconds, @@ -266,14 +289,21 @@ class ConversionServiceImpl(conversion_connect.ConversionService): ) logger.info( "Resolved conversion plan conversion_id=%s source_filename=%s " - "resolution=%s inferred_dpi=%d output_size=%dx%d " + "full[resolution=%s,size=%dx%d,jpeg_quality=%d] " + "thumbnail[resolution=%s,size=%dx%d,jpeg_quality=%d] " + "inferred_dpi=%d " "computed_timeouts_s[pptx_to_pdf_total=%d,pdf_to_images_total=%d,pdf_to_images_per_page=%d]", session.conversion_id, session.source_filename, - conversion_pb2.ConversionResolution.Name(session.resolution), - result.inferred_dpi, + conversion_pb2.ConversionResolution.Name(session.full_resolution), result.width, result.height, + session.full_jpeg_quality, + conversion_pb2.ConversionResolution.Name(session.thumbnail_resolution), + result.thumbnail_width, + result.thumbnail_height, + session.thumbnail_jpeg_quality, + result.inferred_dpi, result.pptx_to_pdf_timeout_s, result.pdf_to_images_timeout_s, result.pdf_to_images_page_timeout_s, @@ -282,7 +312,7 @@ class ConversionServiceImpl(conversion_connect.ConversionService): session, phase=conversion_pb2.CONVERSION_PHASE_UPLOADING_RESULTS, current_progress=0, - max_progress=len(result.slides), + max_progress=len(result.slides) * 2, ) session.slide_deck = await asyncio.to_thread( self._upload_and_build_slide_deck, @@ -291,6 +321,8 @@ class ConversionServiceImpl(conversion_connect.ConversionService): result.source_filename, result.width, result.height, + result.thumbnail_width, + result.thumbnail_height, lambda current, max_value: self._set_session_progress( session, phase=conversion_pb2.CONVERSION_PHASE_UPLOADING_RESULTS, @@ -358,12 +390,16 @@ class ConversionServiceImpl(conversion_connect.ConversionService): source_filename: str, width: int, height: int, + thumbnail_width: int, + thumbnail_height: int, progress_callback: Callable[[int, int], None] | None = None, ) -> conversion_pb2.SlideDeck: """Upload generated slide images and construct API response payload.""" response_slides: list[conversion_pb2.Slide] = [] slide_total = len(slides) - for slide_index, slide in enumerate(slides, start=1): + upload_total = slide_total * 2 + upload_index = 0 + for slide in slides: object_key = f"output/slide-{slide.index:04d}{slide.image_path.suffix}" self._store.fput_object(session.bucket_name, object_key, slide.image_path) image_url = self._store.presigned_get_url( @@ -371,15 +407,33 @@ class ConversionServiceImpl(conversion_connect.ConversionService): object_key, ttl_seconds=self._config.s3_session_ttl_seconds, ) + upload_index += 1 + if progress_callback is not None: + progress_callback(upload_index, upload_total) + thumbnail_object_key = ( + f"output/thumb/slide-{slide.index:04d}{slide.thumbnail_path.suffix}" + ) + self._store.fput_object( + session.bucket_name, + thumbnail_object_key, + slide.thumbnail_path, + ) + thumbnail_image_url = self._store.presigned_get_url( + session.bucket_name, + thumbnail_object_key, + ttl_seconds=self._config.s3_session_ttl_seconds, + ) + upload_index += 1 response_slides.append( conversion_pb2.Slide( index=slide.index, notes_plain=slide.notes_plain, image_url=image_url, + thumbnail_image_url=thumbnail_image_url, ) ) if progress_callback is not None: - progress_callback(slide_index, slide_total) + progress_callback(upload_index, upload_total) return conversion_pb2.SlideDeck( conversion_id=session.conversion_id, @@ -388,8 +442,38 @@ class ConversionServiceImpl(conversion_connect.ConversionService): created_at=_to_timestamp(utc_now()), width=width, height=height, + thumbnail_width=thumbnail_width, + thumbnail_height=thumbnail_height, ) + def _resolve_raster_options( + self, + options: conversion_pb2.SlideRasterOptions, + *, + default_resolution: conversion_pb2.ConversionResolution, + default_jpeg_quality: int, + field_name: str, + ) -> tuple[conversion_pb2.ConversionResolution, int]: + """Resolve per-tier raster options with defaults and validation.""" + resolution = options.resolution + if resolution == conversion_pb2.CONVERSION_RESOLUTION_UNSPECIFIED: + resolution = default_resolution + if resolution not in _RESOLUTION_PRESET_BY_PROTO: + raise ConnectError(Code.INVALID_ARGUMENT, f"{field_name}.resolution is invalid") + jpeg_quality = default_jpeg_quality + if options.HasField("jpeg"): + quality = options.jpeg.quality + if quality == 0: + jpeg_quality = default_jpeg_quality + elif 1 <= quality <= 100: + jpeg_quality = quality + else: + raise ConnectError( + Code.INVALID_ARGUMENT, + f"{field_name}.jpeg.quality must be 0 or between 1 and 100", + ) + return resolution, jpeg_quality + async def _delayed_cleanup(self, session: ConversionSession) -> None: """Delete storage resources after the configured session retention period.""" try: