228 lines
6.3 KiB
Go
228 lines
6.3 KiB
Go
// Package officeconvertclient provides typed Connect RPC helpers for conversions.
|
|
package officeconvertclient
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
"time"
|
|
|
|
"connectrpc.com/connect"
|
|
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
|
|
}
|
|
|
|
// HtmlFormattingPolicy configures which formatting features are ignored in HTML notes mode.
|
|
type HtmlFormattingPolicy struct {
|
|
IgnoreBold bool
|
|
IgnoreItalic bool
|
|
IgnoreUnderline bool
|
|
IgnoreStrikethrough bool
|
|
IgnoreFontSize bool
|
|
IgnoreColor bool
|
|
}
|
|
|
|
// NotesOptions controls speaker-notes extraction/output.
|
|
type NotesOptions struct {
|
|
Format officeconvertapiv1.NotesFormat
|
|
HTMLUseParagraphTags *bool
|
|
HTMLPolicy *HtmlFormattingPolicy
|
|
}
|
|
|
|
// CreateConversionOptions configures optional full and thumbnail raster tiers.
|
|
type CreateConversionOptions struct {
|
|
Full *RasterTierOptions
|
|
Thumbnail *RasterTierOptions
|
|
Notes *NotesOptions
|
|
}
|
|
|
|
// Client wraps the generated Connect client with orchestration-focused helpers.
|
|
type Client struct {
|
|
rpc officeconvertapiv1connect.ConversionServiceClient
|
|
httpClient *http.Client
|
|
baseURL string
|
|
pollInterval time.Duration
|
|
}
|
|
|
|
// NewClient creates a new typed Officeconvert client.
|
|
func NewClient(baseURL string, httpClient *http.Client, options ...connect.ClientOption) *Client {
|
|
if httpClient == nil {
|
|
httpClient = http.DefaultClient
|
|
}
|
|
return &Client{
|
|
rpc: officeconvertapiv1connect.NewConversionServiceClient(httpClient, baseURL, options...),
|
|
httpClient: httpClient,
|
|
baseURL: baseURL,
|
|
pollInterval: defaultPollInterval,
|
|
}
|
|
}
|
|
|
|
// SetPollInterval configures the polling cadence used by WaitForCompletion.
|
|
func (c *Client) SetPollInterval(interval time.Duration) {
|
|
if interval > 0 {
|
|
c.pollInterval = interval
|
|
}
|
|
}
|
|
|
|
// CreateConversion starts a conversion session and returns upload metadata.
|
|
func (c *Client) CreateConversion(
|
|
ctx context.Context,
|
|
sourceFilename string,
|
|
options *CreateConversionOptions,
|
|
) (*CreateConversionResponse, error) {
|
|
message := &officeconvertapiv1.CreateConversionRequest{
|
|
SourceFilename: sourceFilename,
|
|
}
|
|
if options != nil {
|
|
message.Full = toProtoSlideRasterOptions(options.Full)
|
|
message.Thumbnail = toProtoSlideRasterOptions(options.Thumbnail)
|
|
message.Notes = toProtoNotesOptions(options.Notes)
|
|
}
|
|
req := connect.NewRequest(message)
|
|
res, err := c.rpc.CreateConversion(ctx, req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
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
|
|
}
|
|
|
|
func toProtoNotesOptions(options *NotesOptions) *officeconvertapiv1.NotesOptions {
|
|
if options == nil {
|
|
return nil
|
|
}
|
|
proto := &officeconvertapiv1.NotesOptions{
|
|
Format: options.Format,
|
|
}
|
|
if options.HTMLUseParagraphTags != nil {
|
|
proto.HtmlUseParagraphTags = options.HTMLUseParagraphTags
|
|
}
|
|
if options.HTMLPolicy != nil {
|
|
proto.HtmlPolicy = &officeconvertapiv1.HtmlFormattingPolicy{
|
|
IgnoreBold: options.HTMLPolicy.IgnoreBold,
|
|
IgnoreItalic: options.HTMLPolicy.IgnoreItalic,
|
|
IgnoreUnderline: options.HTMLPolicy.IgnoreUnderline,
|
|
IgnoreStrikethrough: options.HTMLPolicy.IgnoreStrikethrough,
|
|
IgnoreFontSize: options.HTMLPolicy.IgnoreFontSize,
|
|
IgnoreColor: options.HTMLPolicy.IgnoreColor,
|
|
}
|
|
}
|
|
return proto
|
|
}
|
|
|
|
// StartConversion signals that upload is complete and conversion can begin.
|
|
func (c *Client) StartConversion(
|
|
ctx context.Context,
|
|
id ksuid.KSUID,
|
|
) (*StartConversionResponse, error) {
|
|
req := connect.NewRequest(&officeconvertapiv1.StartConversionRequest{
|
|
ConversionId: id.String(),
|
|
})
|
|
res, err := c.rpc.StartConversion(ctx, req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return augmentStartConversionResponse(res.Msg)
|
|
}
|
|
|
|
// GetConversionStatus returns the latest status for a conversion session.
|
|
func (c *Client) GetConversionStatus(
|
|
ctx context.Context,
|
|
id ksuid.KSUID,
|
|
) (*GetConversionStatusResponse, error) {
|
|
req := connect.NewRequest(&officeconvertapiv1.GetConversionStatusRequest{
|
|
ConversionId: id.String(),
|
|
})
|
|
res, err := c.rpc.GetConversionStatus(ctx, req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return augmentGetConversionStatusResponse(res.Msg)
|
|
}
|
|
|
|
// GetSlideDeck retrieves the final converted deck response.
|
|
func (c *Client) GetSlideDeck(
|
|
ctx context.Context,
|
|
id ksuid.KSUID,
|
|
) (*officeconvertapiv1.GetSlideDeckResponse, error) {
|
|
req := connect.NewRequest(&officeconvertapiv1.GetSlideDeckRequest{
|
|
ConversionId: id.String(),
|
|
})
|
|
res, err := c.rpc.GetSlideDeck(ctx, req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return res.Msg, nil
|
|
}
|
|
|
|
// DeleteConversion triggers immediate resource cleanup for a session.
|
|
func (c *Client) DeleteConversion(
|
|
ctx context.Context,
|
|
id ksuid.KSUID,
|
|
) (*DeleteConversionResponse, error) {
|
|
req := connect.NewRequest(&officeconvertapiv1.DeleteConversionRequest{
|
|
ConversionId: id.String(),
|
|
})
|
|
res, err := c.rpc.DeleteConversion(ctx, req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return augmentDeleteConversionResponse(res.Msg)
|
|
}
|
|
|
|
// WaitForCompletion polls status until terminal completion or context cancellation.
|
|
func (c *Client) WaitForCompletion(
|
|
ctx context.Context,
|
|
id ksuid.KSUID,
|
|
) (*GetConversionStatusResponse, error) {
|
|
ticker := time.NewTicker(c.pollInterval)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
status, err := c.GetConversionStatus(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch status.Status {
|
|
case officeconvertapiv1.ConversionStatus_CONVERSION_STATUS_SUCCEEDED:
|
|
return status, nil
|
|
case officeconvertapiv1.ConversionStatus_CONVERSION_STATUS_FAILED:
|
|
return nil, errors.New(status.ErrorMessage)
|
|
}
|
|
|
|
select {
|
|
case <-ctx.Done():
|
|
return nil, ctx.Err()
|
|
case <-ticker.C:
|
|
}
|
|
}
|
|
}
|