mvp implementation
This commit is contained in:
@@ -0,0 +1,9 @@
|
|||||||
|
MINIO_ROOT_USER=minioadmin
|
||||||
|
MINIO_ROOT_PASSWORD=minioadmin
|
||||||
|
MINIO_ENDPOINT=minio:9000
|
||||||
|
MINIO_PUBLIC_ENDPOINT=localhost:9000
|
||||||
|
MINIO_USE_SSL=false
|
||||||
|
MINIO_ACCESS_KEY=minioadmin
|
||||||
|
MINIO_SECRET_KEY=minioadmin
|
||||||
|
MINIO_SESSION_TTL_SECONDS=3600
|
||||||
|
CONVERSION_CLEANUP_DELAY_SECONDS=3600
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
FROM python:3.12-slim
|
||||||
|
|
||||||
|
ENV PYTHONDONTWRITEBYTECODE=1
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
ENV PYTHONPATH=/app/gen/python:/app/python/packages/officeconvert/src:/app/python/packages/server/src
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
libreoffice \
|
||||||
|
poppler-utils \
|
||||||
|
fonts-dejavu-core \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY python /app/python
|
||||||
|
COPY gen /app/gen
|
||||||
|
|
||||||
|
RUN pip install --no-cache-dir -e /app/python/packages/officeconvert -e /app/python/packages/server
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
CMD ["uvicorn", "officeconvert_server.app:app", "--host", "0.0.0.0", "--port", "8080"]
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
SHELL := /bin/sh
|
||||||
|
|
||||||
|
BUF ?= /Users/end/go/bin/buf
|
||||||
|
|
||||||
|
.PHONY: buf-lint buf-generate py-sync go-test compose-up compose-up-dev
|
||||||
|
|
||||||
|
buf-lint:
|
||||||
|
"$(BUF)" lint
|
||||||
|
|
||||||
|
buf-generate:
|
||||||
|
"$(BUF)" generate
|
||||||
|
|
||||||
|
py-sync:
|
||||||
|
uv sync --project python
|
||||||
|
|
||||||
|
go-test:
|
||||||
|
cd clients/go && go test ./...
|
||||||
|
|
||||||
|
compose-up:
|
||||||
|
docker compose --env-file .env.example -f deploy/docker-compose.yml up --build
|
||||||
|
|
||||||
|
compose-up-dev:
|
||||||
|
docker compose --env-file .env.example -f deploy/docker-compose.dev.yml up
|
||||||
@@ -1,13 +1,33 @@
|
|||||||
# officeconvert
|
# officeconvert
|
||||||
|
|
||||||
officeconvert is a toolset to convert common office document types images using LibreOffice (`soffice`). This repository provides three classes of projects:
|
officeconvert is a multimodule conversion toolkit for turning presentation files into
|
||||||
|
typed `SlideDeck` artifacts with rendered slide images and notes. The repository is
|
||||||
|
organized around Protocol Buffer schemas with ConnectRPC code generation for both server
|
||||||
|
and client compatibility.
|
||||||
|
|
||||||
- Python library responsible for actual conversion work
|
## Modules
|
||||||
- Connect gRPC Python server for easy microservice deployment
|
|
||||||
- Client libraries for assorted languages
|
|
||||||
|
|
||||||
APIs are built on typed Protocol Buffer schemas with codegen for the server and clients based on the primary schema.
|
- `proto/` contains protobuf schemas and RPC definitions.
|
||||||
|
- `gen/python` and `gen/go` contain generated protocol and Connect code.
|
||||||
|
- `python/packages/officeconvert` is the core conversion library (PPTX -> PDF -> images + notes).
|
||||||
|
- `python/packages/server` is the ConnectRPC Python server with MinIO orchestration.
|
||||||
|
- `clients/go` is the first client library with layered orchestration helpers.
|
||||||
|
- `deploy/` contains production-ish and dev Docker Compose files.
|
||||||
|
|
||||||
## Supported Document Types
|
## Supported Document Types
|
||||||
|
|
||||||
Currently, only PPTX and ODP documents are supported. At the time of writing, PDF and image conversion is planned, however, presentation documents can currently only be converted to a custom `SlideDeck` type that encodes slides as images and includes richly-formatted slide notes.
|
MVP currently supports **PPTX only** and produces a `SlideDeck` result containing:
|
||||||
|
|
||||||
|
- ordered slide image URLs
|
||||||
|
- plain-text notes per slide
|
||||||
|
|
||||||
|
## Quick Commands
|
||||||
|
|
||||||
|
Use the root `Makefile`:
|
||||||
|
|
||||||
|
- `make buf-lint` to lint protobufs
|
||||||
|
- `make buf-generate` to regenerate Go and Python types
|
||||||
|
- `make py-sync` to sync Python workspace dependencies with uv
|
||||||
|
- `make go-test` to run Go client tests
|
||||||
|
- `make compose-up` to run server + MinIO
|
||||||
|
- `make compose-up-dev` to run MinIO only
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
version: v2
|
||||||
|
plugins:
|
||||||
|
- remote: buf.build/protocolbuffers/python
|
||||||
|
out: gen/python
|
||||||
|
- remote: buf.build/protocolbuffers/pyi
|
||||||
|
out: gen/python
|
||||||
|
- remote: buf.build/connectrpc/python
|
||||||
|
out: gen/python
|
||||||
|
- remote: buf.build/protocolbuffers/go
|
||||||
|
out: gen/go
|
||||||
|
opt:
|
||||||
|
- paths=source_relative
|
||||||
|
- remote: buf.build/connectrpc/go
|
||||||
|
out: gen/go
|
||||||
|
opt:
|
||||||
|
- paths=source_relative
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
version: v2
|
||||||
|
modules:
|
||||||
|
- path: proto
|
||||||
|
lint:
|
||||||
|
use:
|
||||||
|
- STANDARD
|
||||||
|
breaking:
|
||||||
|
use:
|
||||||
|
- FILE
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
module github.com/end/officeconvert/clients/go
|
||||||
|
|
||||||
|
go 1.25.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
connectrpc.com/connect v1.19.1
|
||||||
|
github.com/end/officeconvert v0.0.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require google.golang.org/protobuf v1.36.11 // indirect
|
||||||
|
|
||||||
|
replace github.com/end/officeconvert => ../..
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14=
|
||||||
|
connectrpc.com/connect v1.19.1/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w=
|
||||||
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
|
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
// Package officeconvertclient provides typed Connect RPC helpers for conversions.
|
||||||
|
package officeconvertclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"connectrpc.com/connect"
|
||||||
|
officeconvertapiv1 "github.com/end/officeconvert/gen/go/officeconvertapi/v1"
|
||||||
|
"github.com/end/officeconvert/gen/go/officeconvertapi/v1/officeconvertapiv1connect"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultPollInterval = 2 * time.Second
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
) (*officeconvertapiv1.CreateConversionResponse, error) {
|
||||||
|
req := connect.NewRequest(&officeconvertapiv1.CreateConversionRequest{
|
||||||
|
SourceFilename: sourceFilename,
|
||||||
|
})
|
||||||
|
res, err := c.rpc.CreateConversion(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return res.Msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartConversion signals that upload is complete and conversion can begin.
|
||||||
|
func (c *Client) StartConversion(
|
||||||
|
ctx context.Context,
|
||||||
|
conversionID string,
|
||||||
|
) (*officeconvertapiv1.StartConversionResponse, error) {
|
||||||
|
req := connect.NewRequest(&officeconvertapiv1.StartConversionRequest{
|
||||||
|
ConversionId: conversionID,
|
||||||
|
})
|
||||||
|
res, err := c.rpc.StartConversion(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return res.Msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConversionStatus returns the latest status for a conversion session.
|
||||||
|
func (c *Client) GetConversionStatus(
|
||||||
|
ctx context.Context,
|
||||||
|
conversionID string,
|
||||||
|
) (*officeconvertapiv1.GetConversionStatusResponse, error) {
|
||||||
|
req := connect.NewRequest(&officeconvertapiv1.GetConversionStatusRequest{
|
||||||
|
ConversionId: conversionID,
|
||||||
|
})
|
||||||
|
res, err := c.rpc.GetConversionStatus(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return res.Msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSlideDeck retrieves the final converted deck response.
|
||||||
|
func (c *Client) GetSlideDeck(
|
||||||
|
ctx context.Context,
|
||||||
|
conversionID string,
|
||||||
|
) (*officeconvertapiv1.GetSlideDeckResponse, error) {
|
||||||
|
req := connect.NewRequest(&officeconvertapiv1.GetSlideDeckRequest{
|
||||||
|
ConversionId: conversionID,
|
||||||
|
})
|
||||||
|
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,
|
||||||
|
conversionID string,
|
||||||
|
) (*officeconvertapiv1.DeleteConversionResponse, error) {
|
||||||
|
req := connect.NewRequest(&officeconvertapiv1.DeleteConversionRequest{
|
||||||
|
ConversionId: conversionID,
|
||||||
|
})
|
||||||
|
res, err := c.rpc.DeleteConversion(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return res.Msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitForCompletion polls status until terminal completion or context cancellation.
|
||||||
|
func (c *Client) WaitForCompletion(
|
||||||
|
ctx context.Context,
|
||||||
|
conversionID string,
|
||||||
|
) (*officeconvertapiv1.GetConversionStatusResponse, error) {
|
||||||
|
ticker := time.NewTicker(c.pollInterval)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
status, err := c.GetConversionStatus(ctx, conversionID)
|
||||||
|
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:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package officeconvertclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
officeconvertapiv1 "github.com/end/officeconvert/gen/go/officeconvertapi/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DownloadedArtifact records a downloaded slide image destination path.
|
||||||
|
type DownloadedArtifact struct {
|
||||||
|
SlideIndex int32
|
||||||
|
LocalPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
// DownloadArtifacts fetches all slide image URLs from a slide deck into outputDir.
|
||||||
|
func (c *Client) DownloadArtifacts(
|
||||||
|
ctx context.Context,
|
||||||
|
slideDeck *officeconvertapiv1.SlideDeck,
|
||||||
|
outputDir string,
|
||||||
|
) ([]DownloadedArtifact, error) {
|
||||||
|
if slideDeck == nil {
|
||||||
|
return nil, fmt.Errorf("slide deck is nil")
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(outputDir, 0o755); err != nil {
|
||||||
|
return nil, fmt.Errorf("create output directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
downloaded := make([]DownloadedArtifact, 0, len(slideDeck.Slides))
|
||||||
|
for _, slide := range slideDeck.Slides {
|
||||||
|
localPath := filepath.Join(outputDir, fmt.Sprintf("slide-%04d%s", slide.Index, inferImageExt(slide.ImageUrl)))
|
||||||
|
if err := c.downloadFile(ctx, slide.ImageUrl, localPath); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
downloaded = append(downloaded, DownloadedArtifact{
|
||||||
|
SlideIndex: slide.Index,
|
||||||
|
LocalPath: localPath,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return downloaded, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) downloadFile(ctx context.Context, sourceURL string, destinationPath string) error {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, sourceURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("build download request: %w", err)
|
||||||
|
}
|
||||||
|
res, err := c.httpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("download artifact: %w", err)
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
if res.StatusCode < 200 || res.StatusCode >= 300 {
|
||||||
|
return fmt.Errorf("download failed with status %d", res.StatusCode)
|
||||||
|
}
|
||||||
|
file, err := os.Create(destinationPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create artifact file: %w", err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
if _, err := io.Copy(file, res.Body); err != nil {
|
||||||
|
return fmt.Errorf("write artifact file: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func inferImageExt(imageURL string) string {
|
||||||
|
switch {
|
||||||
|
case strings.Contains(imageURL, ".jpeg") || strings.Contains(imageURL, ".jpg"):
|
||||||
|
return ".jpg"
|
||||||
|
case strings.Contains(imageURL, ".webp"):
|
||||||
|
return ".webp"
|
||||||
|
default:
|
||||||
|
return ".png"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package officeconvertclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
officeconvertapiv1 "github.com/end/officeconvert/gen/go/officeconvertapi/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConversionResult groups the terminal status and deck payload for one conversion.
|
||||||
|
type ConversionResult struct {
|
||||||
|
Status *officeconvertapiv1.GetConversionStatusResponse
|
||||||
|
Deck *officeconvertapiv1.SlideDeck
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertPPTXFile runs the full create-upload-start-wait-fetch flow.
|
||||||
|
func (c *Client) ConvertPPTXFile(ctx context.Context, localPPTXPath string) (*ConversionResult, error) {
|
||||||
|
createRes, err := c.CreateConversion(ctx, filepath.Base(localPPTXPath))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("create conversion: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.UploadPPTX(ctx, createRes.UploadUrl, localPPTXPath); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := c.StartConversion(ctx, createRes.ConversionId); err != nil {
|
||||||
|
return nil, fmt.Errorf("start conversion: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
statusRes, err := c.WaitForCompletion(ctx, createRes.ConversionId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("wait for completion: %w", err)
|
||||||
|
}
|
||||||
|
deckRes, err := c.GetSlideDeck(ctx, createRes.ConversionId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("get slide deck: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ConversionResult{
|
||||||
|
Status: statusRes,
|
||||||
|
Deck: deckRes.SlideDeck,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package officeconvertclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UploadPPTX uploads a local PPTX using the presigned URL from CreateConversion.
|
||||||
|
func (c *Client) UploadPPTX(
|
||||||
|
ctx context.Context,
|
||||||
|
uploadURL string,
|
||||||
|
localPPTXPath string,
|
||||||
|
) error {
|
||||||
|
file, err := os.Open(localPPTXPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("open pptx: %w", err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
return c.uploadReader(ctx, uploadURL, file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) uploadReader(ctx context.Context, uploadURL string, body io.Reader) error {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodPut, uploadURL, body)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("build upload request: %w", err)
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/vnd.openxmlformats-officedocument.presentationml.presentation")
|
||||||
|
|
||||||
|
res, err := c.httpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("upload pptx: %w", err)
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
if res.StatusCode < 200 || res.StatusCode >= 300 {
|
||||||
|
return fmt.Errorf("upload failed with status %d", res.StatusCode)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
services:
|
||||||
|
minio:
|
||||||
|
image: minio/minio:RELEASE.2026-02-17T00-53-00Z
|
||||||
|
command: server /data --console-address ":9001"
|
||||||
|
environment:
|
||||||
|
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin}
|
||||||
|
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin}
|
||||||
|
ports:
|
||||||
|
- "9000:9000"
|
||||||
|
- "9001:9001"
|
||||||
|
volumes:
|
||||||
|
- minio_data:/data
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
minio_data:
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
services:
|
||||||
|
minio:
|
||||||
|
image: minio/minio:RELEASE.2026-02-17T00-53-00Z
|
||||||
|
command: server /data --console-address ":9001"
|
||||||
|
environment:
|
||||||
|
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin}
|
||||||
|
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin}
|
||||||
|
ports:
|
||||||
|
- "9000:9000"
|
||||||
|
- "9001:9001"
|
||||||
|
volumes:
|
||||||
|
- minio_data:/data
|
||||||
|
|
||||||
|
server:
|
||||||
|
build:
|
||||||
|
context: ..
|
||||||
|
dockerfile: Dockerfile.server
|
||||||
|
depends_on:
|
||||||
|
- minio
|
||||||
|
environment:
|
||||||
|
MINIO_ENDPOINT: ${MINIO_ENDPOINT:-minio:9000}
|
||||||
|
MINIO_PUBLIC_ENDPOINT: ${MINIO_PUBLIC_ENDPOINT:-localhost:9000}
|
||||||
|
MINIO_USE_SSL: ${MINIO_USE_SSL:-false}
|
||||||
|
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY:-minioadmin}
|
||||||
|
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY:-minioadmin}
|
||||||
|
MINIO_SESSION_TTL_SECONDS: ${MINIO_SESSION_TTL_SECONDS:-3600}
|
||||||
|
CONVERSION_CLEANUP_DELAY_SECONDS: ${CONVERSION_CLEANUP_DELAY_SECONDS:-3600}
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
minio_data:
|
||||||
@@ -0,0 +1,873 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.36.11
|
||||||
|
// protoc (unknown)
|
||||||
|
// source: officeconvertapi/v1/conversion.proto
|
||||||
|
|
||||||
|
package officeconvertapiv1
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
unsafe "unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConversionStatus represents the lifecycle state of a conversion request.
|
||||||
|
type ConversionStatus int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
ConversionStatus_CONVERSION_STATUS_UNSPECIFIED ConversionStatus = 0
|
||||||
|
ConversionStatus_CONVERSION_STATUS_PENDING ConversionStatus = 1
|
||||||
|
ConversionStatus_CONVERSION_STATUS_RUNNING ConversionStatus = 2
|
||||||
|
ConversionStatus_CONVERSION_STATUS_SUCCEEDED ConversionStatus = 3
|
||||||
|
ConversionStatus_CONVERSION_STATUS_FAILED ConversionStatus = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
// Enum value maps for ConversionStatus.
|
||||||
|
var (
|
||||||
|
ConversionStatus_name = map[int32]string{
|
||||||
|
0: "CONVERSION_STATUS_UNSPECIFIED",
|
||||||
|
1: "CONVERSION_STATUS_PENDING",
|
||||||
|
2: "CONVERSION_STATUS_RUNNING",
|
||||||
|
3: "CONVERSION_STATUS_SUCCEEDED",
|
||||||
|
4: "CONVERSION_STATUS_FAILED",
|
||||||
|
}
|
||||||
|
ConversionStatus_value = map[string]int32{
|
||||||
|
"CONVERSION_STATUS_UNSPECIFIED": 0,
|
||||||
|
"CONVERSION_STATUS_PENDING": 1,
|
||||||
|
"CONVERSION_STATUS_RUNNING": 2,
|
||||||
|
"CONVERSION_STATUS_SUCCEEDED": 3,
|
||||||
|
"CONVERSION_STATUS_FAILED": 4,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (x ConversionStatus) Enum() *ConversionStatus {
|
||||||
|
p := new(ConversionStatus)
|
||||||
|
*p = x
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x ConversionStatus) String() string {
|
||||||
|
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ConversionStatus) Descriptor() protoreflect.EnumDescriptor {
|
||||||
|
return file_officeconvertapi_v1_conversion_proto_enumTypes[0].Descriptor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ConversionStatus) Type() protoreflect.EnumType {
|
||||||
|
return &file_officeconvertapi_v1_conversion_proto_enumTypes[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x ConversionStatus) Number() protoreflect.EnumNumber {
|
||||||
|
return protoreflect.EnumNumber(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ConversionStatus.Descriptor instead.
|
||||||
|
func (ConversionStatus) EnumDescriptor() ([]byte, []int) {
|
||||||
|
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Slide) Reset() {
|
||||||
|
*x = Slide{}
|
||||||
|
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Slide) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Slide) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Slide) 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 Slide.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Slide) Descriptor() ([]byte, []int) {
|
||||||
|
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Slide) GetIndex() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Index
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Slide) GetNotesPlain() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.NotesPlain
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Slide) GetImageUrl() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.ImageUrl
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// SlideDeck is the final structured conversion artifact.
|
||||||
|
type SlideDeck struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
|
||||||
|
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"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SlideDeck) Reset() {
|
||||||
|
*x = SlideDeck{}
|
||||||
|
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SlideDeck) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*SlideDeck) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *SlideDeck) 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 SlideDeck.ProtoReflect.Descriptor instead.
|
||||||
|
func (*SlideDeck) Descriptor() ([]byte, []int) {
|
||||||
|
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SlideDeck) GetConversionId() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.ConversionId
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SlideDeck) GetSourceFilename() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.SourceFilename
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SlideDeck) GetSlides() []*Slide {
|
||||||
|
if x != nil {
|
||||||
|
return x.Slides
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *SlideDeck) GetCreatedAt() *timestamppb.Timestamp {
|
||||||
|
if x != nil {
|
||||||
|
return x.CreatedAt
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateConversionRequest) Reset() {
|
||||||
|
*x = CreateConversionRequest{}
|
||||||
|
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[2]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateConversionRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*CreateConversionRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *CreateConversionRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[2]
|
||||||
|
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 CreateConversionRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*CreateConversionRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{2}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateConversionRequest) GetSourceFilename() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.SourceFilename
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateConversionResponse returns upload details for the session.
|
||||||
|
type CreateConversionResponse struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
|
||||||
|
UploadBucket string `protobuf:"bytes,2,opt,name=upload_bucket,json=uploadBucket,proto3" json:"upload_bucket,omitempty"`
|
||||||
|
UploadObjectKey string `protobuf:"bytes,3,opt,name=upload_object_key,json=uploadObjectKey,proto3" json:"upload_object_key,omitempty"`
|
||||||
|
UploadUrl string `protobuf:"bytes,4,opt,name=upload_url,json=uploadUrl,proto3" json:"upload_url,omitempty"`
|
||||||
|
ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=expires_at,json=expiresAt,proto3" json:"expires_at,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateConversionResponse) Reset() {
|
||||||
|
*x = CreateConversionResponse{}
|
||||||
|
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[3]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateConversionResponse) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*CreateConversionResponse) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *CreateConversionResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[3]
|
||||||
|
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 CreateConversionResponse.ProtoReflect.Descriptor instead.
|
||||||
|
func (*CreateConversionResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{3}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateConversionResponse) GetConversionId() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.ConversionId
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateConversionResponse) GetUploadBucket() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.UploadBucket
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateConversionResponse) GetUploadObjectKey() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.UploadObjectKey
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateConversionResponse) GetUploadUrl() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.UploadUrl
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateConversionResponse) GetExpiresAt() *timestamppb.Timestamp {
|
||||||
|
if x != nil {
|
||||||
|
return x.ExpiresAt
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartConversionRequest requests conversion of an already uploaded PPTX.
|
||||||
|
type StartConversionRequest struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *StartConversionRequest) Reset() {
|
||||||
|
*x = StartConversionRequest{}
|
||||||
|
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[4]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *StartConversionRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*StartConversionRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *StartConversionRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[4]
|
||||||
|
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 StartConversionRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*StartConversionRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{4}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *StartConversionRequest) GetConversionId() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.ConversionId
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartConversionResponse returns the first known status after enqueue.
|
||||||
|
type StartConversionResponse struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
|
||||||
|
Status ConversionStatus `protobuf:"varint,2,opt,name=status,proto3,enum=officeconvertapi.v1.ConversionStatus" json:"status,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *StartConversionResponse) Reset() {
|
||||||
|
*x = StartConversionResponse{}
|
||||||
|
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[5]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *StartConversionResponse) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*StartConversionResponse) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *StartConversionResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[5]
|
||||||
|
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 StartConversionResponse.ProtoReflect.Descriptor instead.
|
||||||
|
func (*StartConversionResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{5}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *StartConversionResponse) GetConversionId() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.ConversionId
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *StartConversionResponse) GetStatus() ConversionStatus {
|
||||||
|
if x != nil {
|
||||||
|
return x.Status
|
||||||
|
}
|
||||||
|
return ConversionStatus_CONVERSION_STATUS_UNSPECIFIED
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConversionStatusRequest asks for a specific conversion status.
|
||||||
|
type GetConversionStatusRequest struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GetConversionStatusRequest) Reset() {
|
||||||
|
*x = GetConversionStatusRequest{}
|
||||||
|
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[6]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GetConversionStatusRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*GetConversionStatusRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *GetConversionStatusRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[6]
|
||||||
|
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 GetConversionStatusRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*GetConversionStatusRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{6}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GetConversionStatusRequest) GetConversionId() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.ConversionId
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConversionStatusResponse returns current status and optional error info.
|
||||||
|
type GetConversionStatusResponse struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
|
||||||
|
Status ConversionStatus `protobuf:"varint,2,opt,name=status,proto3,enum=officeconvertapi.v1.ConversionStatus" json:"status,omitempty"`
|
||||||
|
ErrorMessage string `protobuf:"bytes,3,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"`
|
||||||
|
UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GetConversionStatusResponse) Reset() {
|
||||||
|
*x = GetConversionStatusResponse{}
|
||||||
|
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[7]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GetConversionStatusResponse) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*GetConversionStatusResponse) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *GetConversionStatusResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[7]
|
||||||
|
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 GetConversionStatusResponse.ProtoReflect.Descriptor instead.
|
||||||
|
func (*GetConversionStatusResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{7}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GetConversionStatusResponse) GetConversionId() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.ConversionId
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GetConversionStatusResponse) GetStatus() ConversionStatus {
|
||||||
|
if x != nil {
|
||||||
|
return x.Status
|
||||||
|
}
|
||||||
|
return ConversionStatus_CONVERSION_STATUS_UNSPECIFIED
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GetConversionStatusResponse) GetErrorMessage() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.ErrorMessage
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GetConversionStatusResponse) GetUpdatedAt() *timestamppb.Timestamp {
|
||||||
|
if x != nil {
|
||||||
|
return x.UpdatedAt
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSlideDeckRequest fetches a completed deck.
|
||||||
|
type GetSlideDeckRequest struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GetSlideDeckRequest) Reset() {
|
||||||
|
*x = GetSlideDeckRequest{}
|
||||||
|
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[8]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GetSlideDeckRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*GetSlideDeckRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *GetSlideDeckRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[8]
|
||||||
|
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 GetSlideDeckRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*GetSlideDeckRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{8}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GetSlideDeckRequest) GetConversionId() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.ConversionId
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSlideDeckResponse contains the converted slide deck.
|
||||||
|
type GetSlideDeckResponse struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
SlideDeck *SlideDeck `protobuf:"bytes,1,opt,name=slide_deck,json=slideDeck,proto3" json:"slide_deck,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GetSlideDeckResponse) Reset() {
|
||||||
|
*x = GetSlideDeckResponse{}
|
||||||
|
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[9]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GetSlideDeckResponse) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*GetSlideDeckResponse) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *GetSlideDeckResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[9]
|
||||||
|
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 GetSlideDeckResponse.ProtoReflect.Descriptor instead.
|
||||||
|
func (*GetSlideDeckResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{9}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GetSlideDeckResponse) GetSlideDeck() *SlideDeck {
|
||||||
|
if x != nil {
|
||||||
|
return x.SlideDeck
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteConversionRequest requests immediate cleanup.
|
||||||
|
type DeleteConversionRequest struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeleteConversionRequest) Reset() {
|
||||||
|
*x = DeleteConversionRequest{}
|
||||||
|
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[10]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeleteConversionRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*DeleteConversionRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *DeleteConversionRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[10]
|
||||||
|
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 DeleteConversionRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*DeleteConversionRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{10}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeleteConversionRequest) GetConversionId() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.ConversionId
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteConversionResponse confirms cleanup details.
|
||||||
|
type DeleteConversionResponse struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
ConversionId string `protobuf:"bytes,1,opt,name=conversion_id,json=conversionId,proto3" json:"conversion_id,omitempty"`
|
||||||
|
Deleted bool `protobuf:"varint,2,opt,name=deleted,proto3" json:"deleted,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeleteConversionResponse) Reset() {
|
||||||
|
*x = DeleteConversionResponse{}
|
||||||
|
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[11]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeleteConversionResponse) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*DeleteConversionResponse) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *DeleteConversionResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[11]
|
||||||
|
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 DeleteConversionResponse.ProtoReflect.Descriptor instead.
|
||||||
|
func (*DeleteConversionResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{11}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeleteConversionResponse) GetConversionId() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.ConversionId
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeleteConversionResponse) GetDeleted() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.Deleted
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
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" +
|
||||||
|
"\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\"\xc8\x01\n" +
|
||||||
|
"\tSlideDeck\x12#\n" +
|
||||||
|
"\rconversion_id\x18\x01 \x01(\tR\fconversionId\x12'\n" +
|
||||||
|
"\x0fsource_filename\x18\x02 \x01(\tR\x0esourceFilename\x122\n" +
|
||||||
|
"\x06slides\x18\x03 \x03(\v2\x1a.officeconvertapi.v1.SlideR\x06slides\x129\n" +
|
||||||
|
"\n" +
|
||||||
|
"created_at\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\"B\n" +
|
||||||
|
"\x17CreateConversionRequest\x12'\n" +
|
||||||
|
"\x0fsource_filename\x18\x01 \x01(\tR\x0esourceFilename\"\xea\x01\n" +
|
||||||
|
"\x18CreateConversionResponse\x12#\n" +
|
||||||
|
"\rconversion_id\x18\x01 \x01(\tR\fconversionId\x12#\n" +
|
||||||
|
"\rupload_bucket\x18\x02 \x01(\tR\fuploadBucket\x12*\n" +
|
||||||
|
"\x11upload_object_key\x18\x03 \x01(\tR\x0fuploadObjectKey\x12\x1d\n" +
|
||||||
|
"\n" +
|
||||||
|
"upload_url\x18\x04 \x01(\tR\tuploadUrl\x129\n" +
|
||||||
|
"\n" +
|
||||||
|
"expires_at\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\texpiresAt\"=\n" +
|
||||||
|
"\x16StartConversionRequest\x12#\n" +
|
||||||
|
"\rconversion_id\x18\x01 \x01(\tR\fconversionId\"}\n" +
|
||||||
|
"\x17StartConversionResponse\x12#\n" +
|
||||||
|
"\rconversion_id\x18\x01 \x01(\tR\fconversionId\x12=\n" +
|
||||||
|
"\x06status\x18\x02 \x01(\x0e2%.officeconvertapi.v1.ConversionStatusR\x06status\"A\n" +
|
||||||
|
"\x1aGetConversionStatusRequest\x12#\n" +
|
||||||
|
"\rconversion_id\x18\x01 \x01(\tR\fconversionId\"\xe1\x01\n" +
|
||||||
|
"\x1bGetConversionStatusResponse\x12#\n" +
|
||||||
|
"\rconversion_id\x18\x01 \x01(\tR\fconversionId\x12=\n" +
|
||||||
|
"\x06status\x18\x02 \x01(\x0e2%.officeconvertapi.v1.ConversionStatusR\x06status\x12#\n" +
|
||||||
|
"\rerror_message\x18\x03 \x01(\tR\ferrorMessage\x129\n" +
|
||||||
|
"\n" +
|
||||||
|
"updated_at\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAt\":\n" +
|
||||||
|
"\x13GetSlideDeckRequest\x12#\n" +
|
||||||
|
"\rconversion_id\x18\x01 \x01(\tR\fconversionId\"U\n" +
|
||||||
|
"\x14GetSlideDeckResponse\x12=\n" +
|
||||||
|
"\n" +
|
||||||
|
"slide_deck\x18\x01 \x01(\v2\x1e.officeconvertapi.v1.SlideDeckR\tslideDeck\">\n" +
|
||||||
|
"\x17DeleteConversionRequest\x12#\n" +
|
||||||
|
"\rconversion_id\x18\x01 \x01(\tR\fconversionId\"Y\n" +
|
||||||
|
"\x18DeleteConversionResponse\x12#\n" +
|
||||||
|
"\rconversion_id\x18\x01 \x01(\tR\fconversionId\x12\x18\n" +
|
||||||
|
"\adeleted\x18\x02 \x01(\bR\adeleted*\xb2\x01\n" +
|
||||||
|
"\x10ConversionStatus\x12!\n" +
|
||||||
|
"\x1dCONVERSION_STATUS_UNSPECIFIED\x10\x00\x12\x1d\n" +
|
||||||
|
"\x19CONVERSION_STATUS_PENDING\x10\x01\x12\x1d\n" +
|
||||||
|
"\x19CONVERSION_STATUS_RUNNING\x10\x02\x12\x1f\n" +
|
||||||
|
"\x1bCONVERSION_STATUS_SUCCEEDED\x10\x03\x12\x1c\n" +
|
||||||
|
"\x18CONVERSION_STATUS_FAILED\x10\x042\xcc\x04\n" +
|
||||||
|
"\x11ConversionService\x12q\n" +
|
||||||
|
"\x10CreateConversion\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\x1a0.officeconvertapi.v1.GetConversionStatusResponse\"\x00\x12e\n" +
|
||||||
|
"\fGetSlideDeck\x12(.officeconvertapi.v1.GetSlideDeckRequest\x1a).officeconvertapi.v1.GetSlideDeckResponse\"\x00\x12q\n" +
|
||||||
|
"\x10DeleteConversion\x12,.officeconvertapi.v1.DeleteConversionRequest\x1a-.officeconvertapi.v1.DeleteConversionResponse\"\x00BLZJgithub.com/end/officeconvert/gen/go/officeconvertapi/v1;officeconvertapiv1b\x06proto3"
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_officeconvertapi_v1_conversion_proto_rawDescOnce sync.Once
|
||||||
|
file_officeconvertapi_v1_conversion_proto_rawDescData []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_officeconvertapi_v1_conversion_proto_rawDescGZIP() []byte {
|
||||||
|
file_officeconvertapi_v1_conversion_proto_rawDescOnce.Do(func() {
|
||||||
|
file_officeconvertapi_v1_conversion_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_officeconvertapi_v1_conversion_proto_rawDesc), len(file_officeconvertapi_v1_conversion_proto_rawDesc)))
|
||||||
|
})
|
||||||
|
return file_officeconvertapi_v1_conversion_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_officeconvertapi_v1_conversion_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||||
|
var file_officeconvertapi_v1_conversion_proto_msgTypes = make([]protoimpl.MessageInfo, 12)
|
||||||
|
var file_officeconvertapi_v1_conversion_proto_goTypes = []any{
|
||||||
|
(ConversionStatus)(0), // 0: officeconvertapi.v1.ConversionStatus
|
||||||
|
(*Slide)(nil), // 1: officeconvertapi.v1.Slide
|
||||||
|
(*SlideDeck)(nil), // 2: officeconvertapi.v1.SlideDeck
|
||||||
|
(*CreateConversionRequest)(nil), // 3: officeconvertapi.v1.CreateConversionRequest
|
||||||
|
(*CreateConversionResponse)(nil), // 4: officeconvertapi.v1.CreateConversionResponse
|
||||||
|
(*StartConversionRequest)(nil), // 5: officeconvertapi.v1.StartConversionRequest
|
||||||
|
(*StartConversionResponse)(nil), // 6: officeconvertapi.v1.StartConversionResponse
|
||||||
|
(*GetConversionStatusRequest)(nil), // 7: officeconvertapi.v1.GetConversionStatusRequest
|
||||||
|
(*GetConversionStatusResponse)(nil), // 8: officeconvertapi.v1.GetConversionStatusResponse
|
||||||
|
(*GetSlideDeckRequest)(nil), // 9: officeconvertapi.v1.GetSlideDeckRequest
|
||||||
|
(*GetSlideDeckResponse)(nil), // 10: officeconvertapi.v1.GetSlideDeckResponse
|
||||||
|
(*DeleteConversionRequest)(nil), // 11: officeconvertapi.v1.DeleteConversionRequest
|
||||||
|
(*DeleteConversionResponse)(nil), // 12: officeconvertapi.v1.DeleteConversionResponse
|
||||||
|
(*timestamppb.Timestamp)(nil), // 13: google.protobuf.Timestamp
|
||||||
|
}
|
||||||
|
var file_officeconvertapi_v1_conversion_proto_depIdxs = []int32{
|
||||||
|
1, // 0: officeconvertapi.v1.SlideDeck.slides:type_name -> officeconvertapi.v1.Slide
|
||||||
|
13, // 1: officeconvertapi.v1.SlideDeck.created_at:type_name -> google.protobuf.Timestamp
|
||||||
|
13, // 2: officeconvertapi.v1.CreateConversionResponse.expires_at:type_name -> google.protobuf.Timestamp
|
||||||
|
0, // 3: officeconvertapi.v1.StartConversionResponse.status:type_name -> officeconvertapi.v1.ConversionStatus
|
||||||
|
0, // 4: officeconvertapi.v1.GetConversionStatusResponse.status:type_name -> officeconvertapi.v1.ConversionStatus
|
||||||
|
13, // 5: officeconvertapi.v1.GetConversionStatusResponse.updated_at:type_name -> google.protobuf.Timestamp
|
||||||
|
2, // 6: officeconvertapi.v1.GetSlideDeckResponse.slide_deck:type_name -> officeconvertapi.v1.SlideDeck
|
||||||
|
3, // 7: officeconvertapi.v1.ConversionService.CreateConversion:input_type -> officeconvertapi.v1.CreateConversionRequest
|
||||||
|
5, // 8: officeconvertapi.v1.ConversionService.StartConversion:input_type -> officeconvertapi.v1.StartConversionRequest
|
||||||
|
7, // 9: officeconvertapi.v1.ConversionService.GetConversionStatus:input_type -> officeconvertapi.v1.GetConversionStatusRequest
|
||||||
|
9, // 10: officeconvertapi.v1.ConversionService.GetSlideDeck:input_type -> officeconvertapi.v1.GetSlideDeckRequest
|
||||||
|
11, // 11: officeconvertapi.v1.ConversionService.DeleteConversion:input_type -> officeconvertapi.v1.DeleteConversionRequest
|
||||||
|
4, // 12: officeconvertapi.v1.ConversionService.CreateConversion:output_type -> officeconvertapi.v1.CreateConversionResponse
|
||||||
|
6, // 13: officeconvertapi.v1.ConversionService.StartConversion:output_type -> officeconvertapi.v1.StartConversionResponse
|
||||||
|
8, // 14: officeconvertapi.v1.ConversionService.GetConversionStatus:output_type -> officeconvertapi.v1.GetConversionStatusResponse
|
||||||
|
10, // 15: officeconvertapi.v1.ConversionService.GetSlideDeck:output_type -> officeconvertapi.v1.GetSlideDeckResponse
|
||||||
|
12, // 16: officeconvertapi.v1.ConversionService.DeleteConversion:output_type -> officeconvertapi.v1.DeleteConversionResponse
|
||||||
|
12, // [12:17] is the sub-list for method output_type
|
||||||
|
7, // [7:12] is the sub-list for method input_type
|
||||||
|
7, // [7:7] is the sub-list for extension type_name
|
||||||
|
7, // [7:7] is the sub-list for extension extendee
|
||||||
|
0, // [0:7] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_officeconvertapi_v1_conversion_proto_init() }
|
||||||
|
func file_officeconvertapi_v1_conversion_proto_init() {
|
||||||
|
if File_officeconvertapi_v1_conversion_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
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: 1,
|
||||||
|
NumMessages: 12,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 1,
|
||||||
|
},
|
||||||
|
GoTypes: file_officeconvertapi_v1_conversion_proto_goTypes,
|
||||||
|
DependencyIndexes: file_officeconvertapi_v1_conversion_proto_depIdxs,
|
||||||
|
EnumInfos: file_officeconvertapi_v1_conversion_proto_enumTypes,
|
||||||
|
MessageInfos: file_officeconvertapi_v1_conversion_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_officeconvertapi_v1_conversion_proto = out.File
|
||||||
|
file_officeconvertapi_v1_conversion_proto_goTypes = nil
|
||||||
|
file_officeconvertapi_v1_conversion_proto_depIdxs = nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,236 @@
|
|||||||
|
// Code generated by protoc-gen-connect-go. DO NOT EDIT.
|
||||||
|
//
|
||||||
|
// Source: officeconvertapi/v1/conversion.proto
|
||||||
|
|
||||||
|
package officeconvertapiv1connect
|
||||||
|
|
||||||
|
import (
|
||||||
|
connect "connectrpc.com/connect"
|
||||||
|
context "context"
|
||||||
|
errors "errors"
|
||||||
|
v1 "github.com/end/officeconvert/gen/go/officeconvertapi/v1"
|
||||||
|
http "net/http"
|
||||||
|
strings "strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file and the connect package are
|
||||||
|
// compatible. If you get a compiler error that this constant is not defined, this code was
|
||||||
|
// generated with a version of connect newer than the one compiled into your binary. You can fix the
|
||||||
|
// problem by either regenerating this code with an older version of connect or updating the connect
|
||||||
|
// version compiled into your binary.
|
||||||
|
const _ = connect.IsAtLeastVersion1_13_0
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ConversionServiceName is the fully-qualified name of the ConversionService service.
|
||||||
|
ConversionServiceName = "officeconvertapi.v1.ConversionService"
|
||||||
|
)
|
||||||
|
|
||||||
|
// These constants are the fully-qualified names of the RPCs defined in this package. They're
|
||||||
|
// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
|
||||||
|
//
|
||||||
|
// Note that these are different from the fully-qualified method names used by
|
||||||
|
// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
|
||||||
|
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
|
||||||
|
// period.
|
||||||
|
const (
|
||||||
|
// ConversionServiceCreateConversionProcedure is the fully-qualified name of the ConversionService's
|
||||||
|
// CreateConversion RPC.
|
||||||
|
ConversionServiceCreateConversionProcedure = "/officeconvertapi.v1.ConversionService/CreateConversion"
|
||||||
|
// ConversionServiceStartConversionProcedure is the fully-qualified name of the ConversionService's
|
||||||
|
// StartConversion RPC.
|
||||||
|
ConversionServiceStartConversionProcedure = "/officeconvertapi.v1.ConversionService/StartConversion"
|
||||||
|
// ConversionServiceGetConversionStatusProcedure is the fully-qualified name of the
|
||||||
|
// ConversionService's GetConversionStatus RPC.
|
||||||
|
ConversionServiceGetConversionStatusProcedure = "/officeconvertapi.v1.ConversionService/GetConversionStatus"
|
||||||
|
// ConversionServiceGetSlideDeckProcedure is the fully-qualified name of the ConversionService's
|
||||||
|
// GetSlideDeck RPC.
|
||||||
|
ConversionServiceGetSlideDeckProcedure = "/officeconvertapi.v1.ConversionService/GetSlideDeck"
|
||||||
|
// ConversionServiceDeleteConversionProcedure is the fully-qualified name of the ConversionService's
|
||||||
|
// DeleteConversion RPC.
|
||||||
|
ConversionServiceDeleteConversionProcedure = "/officeconvertapi.v1.ConversionService/DeleteConversion"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConversionServiceClient is a client for the officeconvertapi.v1.ConversionService service.
|
||||||
|
type ConversionServiceClient interface {
|
||||||
|
// CreateConversion allocates a short-lived session and upload URL for a PPTX.
|
||||||
|
CreateConversion(context.Context, *connect.Request[v1.CreateConversionRequest]) (*connect.Response[v1.CreateConversionResponse], error)
|
||||||
|
// StartConversion marks upload completion and starts server-side conversion.
|
||||||
|
StartConversion(context.Context, *connect.Request[v1.StartConversionRequest]) (*connect.Response[v1.StartConversionResponse], error)
|
||||||
|
// GetConversionStatus returns state transitions for a conversion session.
|
||||||
|
GetConversionStatus(context.Context, *connect.Request[v1.GetConversionStatusRequest]) (*connect.Response[v1.GetConversionStatusResponse], error)
|
||||||
|
// GetSlideDeck fetches the final slide deck data after successful conversion.
|
||||||
|
GetSlideDeck(context.Context, *connect.Request[v1.GetSlideDeckRequest]) (*connect.Response[v1.GetSlideDeckResponse], error)
|
||||||
|
// DeleteConversion deletes session resources before automatic expiration.
|
||||||
|
DeleteConversion(context.Context, *connect.Request[v1.DeleteConversionRequest]) (*connect.Response[v1.DeleteConversionResponse], error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConversionServiceClient constructs a client for the officeconvertapi.v1.ConversionService
|
||||||
|
// service. By default, it uses the Connect protocol with the binary Protobuf Codec, asks for
|
||||||
|
// gzipped responses, and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply
|
||||||
|
// the connect.WithGRPC() or connect.WithGRPCWeb() options.
|
||||||
|
//
|
||||||
|
// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
|
||||||
|
// http://api.acme.com or https://acme.com/grpc).
|
||||||
|
func NewConversionServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) ConversionServiceClient {
|
||||||
|
baseURL = strings.TrimRight(baseURL, "/")
|
||||||
|
conversionServiceMethods := v1.File_officeconvertapi_v1_conversion_proto.Services().ByName("ConversionService").Methods()
|
||||||
|
return &conversionServiceClient{
|
||||||
|
createConversion: connect.NewClient[v1.CreateConversionRequest, v1.CreateConversionResponse](
|
||||||
|
httpClient,
|
||||||
|
baseURL+ConversionServiceCreateConversionProcedure,
|
||||||
|
connect.WithSchema(conversionServiceMethods.ByName("CreateConversion")),
|
||||||
|
connect.WithClientOptions(opts...),
|
||||||
|
),
|
||||||
|
startConversion: connect.NewClient[v1.StartConversionRequest, v1.StartConversionResponse](
|
||||||
|
httpClient,
|
||||||
|
baseURL+ConversionServiceStartConversionProcedure,
|
||||||
|
connect.WithSchema(conversionServiceMethods.ByName("StartConversion")),
|
||||||
|
connect.WithClientOptions(opts...),
|
||||||
|
),
|
||||||
|
getConversionStatus: connect.NewClient[v1.GetConversionStatusRequest, v1.GetConversionStatusResponse](
|
||||||
|
httpClient,
|
||||||
|
baseURL+ConversionServiceGetConversionStatusProcedure,
|
||||||
|
connect.WithSchema(conversionServiceMethods.ByName("GetConversionStatus")),
|
||||||
|
connect.WithClientOptions(opts...),
|
||||||
|
),
|
||||||
|
getSlideDeck: connect.NewClient[v1.GetSlideDeckRequest, v1.GetSlideDeckResponse](
|
||||||
|
httpClient,
|
||||||
|
baseURL+ConversionServiceGetSlideDeckProcedure,
|
||||||
|
connect.WithSchema(conversionServiceMethods.ByName("GetSlideDeck")),
|
||||||
|
connect.WithClientOptions(opts...),
|
||||||
|
),
|
||||||
|
deleteConversion: connect.NewClient[v1.DeleteConversionRequest, v1.DeleteConversionResponse](
|
||||||
|
httpClient,
|
||||||
|
baseURL+ConversionServiceDeleteConversionProcedure,
|
||||||
|
connect.WithSchema(conversionServiceMethods.ByName("DeleteConversion")),
|
||||||
|
connect.WithClientOptions(opts...),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// conversionServiceClient implements ConversionServiceClient.
|
||||||
|
type conversionServiceClient struct {
|
||||||
|
createConversion *connect.Client[v1.CreateConversionRequest, v1.CreateConversionResponse]
|
||||||
|
startConversion *connect.Client[v1.StartConversionRequest, v1.StartConversionResponse]
|
||||||
|
getConversionStatus *connect.Client[v1.GetConversionStatusRequest, v1.GetConversionStatusResponse]
|
||||||
|
getSlideDeck *connect.Client[v1.GetSlideDeckRequest, v1.GetSlideDeckResponse]
|
||||||
|
deleteConversion *connect.Client[v1.DeleteConversionRequest, v1.DeleteConversionResponse]
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateConversion calls officeconvertapi.v1.ConversionService.CreateConversion.
|
||||||
|
func (c *conversionServiceClient) CreateConversion(ctx context.Context, req *connect.Request[v1.CreateConversionRequest]) (*connect.Response[v1.CreateConversionResponse], error) {
|
||||||
|
return c.createConversion.CallUnary(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartConversion calls officeconvertapi.v1.ConversionService.StartConversion.
|
||||||
|
func (c *conversionServiceClient) StartConversion(ctx context.Context, req *connect.Request[v1.StartConversionRequest]) (*connect.Response[v1.StartConversionResponse], error) {
|
||||||
|
return c.startConversion.CallUnary(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConversionStatus calls officeconvertapi.v1.ConversionService.GetConversionStatus.
|
||||||
|
func (c *conversionServiceClient) GetConversionStatus(ctx context.Context, req *connect.Request[v1.GetConversionStatusRequest]) (*connect.Response[v1.GetConversionStatusResponse], error) {
|
||||||
|
return c.getConversionStatus.CallUnary(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSlideDeck calls officeconvertapi.v1.ConversionService.GetSlideDeck.
|
||||||
|
func (c *conversionServiceClient) GetSlideDeck(ctx context.Context, req *connect.Request[v1.GetSlideDeckRequest]) (*connect.Response[v1.GetSlideDeckResponse], error) {
|
||||||
|
return c.getSlideDeck.CallUnary(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteConversion calls officeconvertapi.v1.ConversionService.DeleteConversion.
|
||||||
|
func (c *conversionServiceClient) DeleteConversion(ctx context.Context, req *connect.Request[v1.DeleteConversionRequest]) (*connect.Response[v1.DeleteConversionResponse], error) {
|
||||||
|
return c.deleteConversion.CallUnary(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConversionServiceHandler is an implementation of the officeconvertapi.v1.ConversionService
|
||||||
|
// service.
|
||||||
|
type ConversionServiceHandler interface {
|
||||||
|
// CreateConversion allocates a short-lived session and upload URL for a PPTX.
|
||||||
|
CreateConversion(context.Context, *connect.Request[v1.CreateConversionRequest]) (*connect.Response[v1.CreateConversionResponse], error)
|
||||||
|
// StartConversion marks upload completion and starts server-side conversion.
|
||||||
|
StartConversion(context.Context, *connect.Request[v1.StartConversionRequest]) (*connect.Response[v1.StartConversionResponse], error)
|
||||||
|
// GetConversionStatus returns state transitions for a conversion session.
|
||||||
|
GetConversionStatus(context.Context, *connect.Request[v1.GetConversionStatusRequest]) (*connect.Response[v1.GetConversionStatusResponse], error)
|
||||||
|
// GetSlideDeck fetches the final slide deck data after successful conversion.
|
||||||
|
GetSlideDeck(context.Context, *connect.Request[v1.GetSlideDeckRequest]) (*connect.Response[v1.GetSlideDeckResponse], error)
|
||||||
|
// DeleteConversion deletes session resources before automatic expiration.
|
||||||
|
DeleteConversion(context.Context, *connect.Request[v1.DeleteConversionRequest]) (*connect.Response[v1.DeleteConversionResponse], error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConversionServiceHandler builds an HTTP handler from the service implementation. It returns
|
||||||
|
// the path on which to mount the handler and the handler itself.
|
||||||
|
//
|
||||||
|
// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
|
||||||
|
// and JSON codecs. They also support gzip compression.
|
||||||
|
func NewConversionServiceHandler(svc ConversionServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
|
||||||
|
conversionServiceMethods := v1.File_officeconvertapi_v1_conversion_proto.Services().ByName("ConversionService").Methods()
|
||||||
|
conversionServiceCreateConversionHandler := connect.NewUnaryHandler(
|
||||||
|
ConversionServiceCreateConversionProcedure,
|
||||||
|
svc.CreateConversion,
|
||||||
|
connect.WithSchema(conversionServiceMethods.ByName("CreateConversion")),
|
||||||
|
connect.WithHandlerOptions(opts...),
|
||||||
|
)
|
||||||
|
conversionServiceStartConversionHandler := connect.NewUnaryHandler(
|
||||||
|
ConversionServiceStartConversionProcedure,
|
||||||
|
svc.StartConversion,
|
||||||
|
connect.WithSchema(conversionServiceMethods.ByName("StartConversion")),
|
||||||
|
connect.WithHandlerOptions(opts...),
|
||||||
|
)
|
||||||
|
conversionServiceGetConversionStatusHandler := connect.NewUnaryHandler(
|
||||||
|
ConversionServiceGetConversionStatusProcedure,
|
||||||
|
svc.GetConversionStatus,
|
||||||
|
connect.WithSchema(conversionServiceMethods.ByName("GetConversionStatus")),
|
||||||
|
connect.WithHandlerOptions(opts...),
|
||||||
|
)
|
||||||
|
conversionServiceGetSlideDeckHandler := connect.NewUnaryHandler(
|
||||||
|
ConversionServiceGetSlideDeckProcedure,
|
||||||
|
svc.GetSlideDeck,
|
||||||
|
connect.WithSchema(conversionServiceMethods.ByName("GetSlideDeck")),
|
||||||
|
connect.WithHandlerOptions(opts...),
|
||||||
|
)
|
||||||
|
conversionServiceDeleteConversionHandler := connect.NewUnaryHandler(
|
||||||
|
ConversionServiceDeleteConversionProcedure,
|
||||||
|
svc.DeleteConversion,
|
||||||
|
connect.WithSchema(conversionServiceMethods.ByName("DeleteConversion")),
|
||||||
|
connect.WithHandlerOptions(opts...),
|
||||||
|
)
|
||||||
|
return "/officeconvertapi.v1.ConversionService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
switch r.URL.Path {
|
||||||
|
case ConversionServiceCreateConversionProcedure:
|
||||||
|
conversionServiceCreateConversionHandler.ServeHTTP(w, r)
|
||||||
|
case ConversionServiceStartConversionProcedure:
|
||||||
|
conversionServiceStartConversionHandler.ServeHTTP(w, r)
|
||||||
|
case ConversionServiceGetConversionStatusProcedure:
|
||||||
|
conversionServiceGetConversionStatusHandler.ServeHTTP(w, r)
|
||||||
|
case ConversionServiceGetSlideDeckProcedure:
|
||||||
|
conversionServiceGetSlideDeckHandler.ServeHTTP(w, r)
|
||||||
|
case ConversionServiceDeleteConversionProcedure:
|
||||||
|
conversionServiceDeleteConversionHandler.ServeHTTP(w, r)
|
||||||
|
default:
|
||||||
|
http.NotFound(w, r)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnimplementedConversionServiceHandler returns CodeUnimplemented from all methods.
|
||||||
|
type UnimplementedConversionServiceHandler struct{}
|
||||||
|
|
||||||
|
func (UnimplementedConversionServiceHandler) CreateConversion(context.Context, *connect.Request[v1.CreateConversionRequest]) (*connect.Response[v1.CreateConversionResponse], error) {
|
||||||
|
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("officeconvertapi.v1.ConversionService.CreateConversion is not implemented"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedConversionServiceHandler) StartConversion(context.Context, *connect.Request[v1.StartConversionRequest]) (*connect.Response[v1.StartConversionResponse], error) {
|
||||||
|
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("officeconvertapi.v1.ConversionService.StartConversion is not implemented"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedConversionServiceHandler) GetConversionStatus(context.Context, *connect.Request[v1.GetConversionStatusRequest]) (*connect.Response[v1.GetConversionStatusResponse], error) {
|
||||||
|
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("officeconvertapi.v1.ConversionService.GetConversionStatus is not implemented"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedConversionServiceHandler) GetSlideDeck(context.Context, *connect.Request[v1.GetSlideDeckRequest]) (*connect.Response[v1.GetSlideDeckResponse], error) {
|
||||||
|
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("officeconvertapi.v1.ConversionService.GetSlideDeck is not implemented"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedConversionServiceHandler) DeleteConversion(context.Context, *connect.Request[v1.DeleteConversionRequest]) (*connect.Response[v1.DeleteConversionResponse], error) {
|
||||||
|
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("officeconvertapi.v1.ConversionService.DeleteConversion is not implemented"))
|
||||||
|
}
|
||||||
@@ -0,0 +1,383 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by https://github.com/connectrpc/connect-python. DO NOT EDIT!
|
||||||
|
# source: officeconvertapi/v1/conversion.proto
|
||||||
|
|
||||||
|
from collections.abc import AsyncGenerator, AsyncIterator, Iterable, Iterator, Mapping
|
||||||
|
from typing import Protocol
|
||||||
|
|
||||||
|
from connectrpc.client import ConnectClient, ConnectClientSync
|
||||||
|
from connectrpc.code import Code
|
||||||
|
from connectrpc.compression import Compression
|
||||||
|
from connectrpc.errors import ConnectError
|
||||||
|
from connectrpc.interceptor import Interceptor, InterceptorSync
|
||||||
|
from connectrpc.method import IdempotencyLevel, MethodInfo
|
||||||
|
from connectrpc.request import Headers, RequestContext
|
||||||
|
from connectrpc.server import ConnectASGIApplication, ConnectWSGIApplication, Endpoint, EndpointSync
|
||||||
|
import officeconvertapi.v1.conversion_pb2 as officeconvertapi_dot_v1_dot_conversion__pb2
|
||||||
|
|
||||||
|
|
||||||
|
class ConversionService(Protocol):
|
||||||
|
async def create_conversion(self, request: officeconvertapi_dot_v1_dot_conversion__pb2.CreateConversionRequest, ctx: RequestContext) -> officeconvertapi_dot_v1_dot_conversion__pb2.CreateConversionResponse:
|
||||||
|
raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
|
||||||
|
|
||||||
|
async def start_conversion(self, request: officeconvertapi_dot_v1_dot_conversion__pb2.StartConversionRequest, ctx: RequestContext) -> officeconvertapi_dot_v1_dot_conversion__pb2.StartConversionResponse:
|
||||||
|
raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
|
||||||
|
|
||||||
|
async def get_conversion_status(self, request: officeconvertapi_dot_v1_dot_conversion__pb2.GetConversionStatusRequest, ctx: RequestContext) -> officeconvertapi_dot_v1_dot_conversion__pb2.GetConversionStatusResponse:
|
||||||
|
raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
|
||||||
|
|
||||||
|
async def get_slide_deck(self, request: officeconvertapi_dot_v1_dot_conversion__pb2.GetSlideDeckRequest, ctx: RequestContext) -> officeconvertapi_dot_v1_dot_conversion__pb2.GetSlideDeckResponse:
|
||||||
|
raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
|
||||||
|
|
||||||
|
async def delete_conversion(self, request: officeconvertapi_dot_v1_dot_conversion__pb2.DeleteConversionRequest, ctx: RequestContext) -> officeconvertapi_dot_v1_dot_conversion__pb2.DeleteConversionResponse:
|
||||||
|
raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
|
||||||
|
|
||||||
|
|
||||||
|
class ConversionServiceASGIApplication(ConnectASGIApplication[ConversionService]):
|
||||||
|
def __init__(self, service: ConversionService | AsyncGenerator[ConversionService], *, interceptors: Iterable[Interceptor]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
|
||||||
|
super().__init__(
|
||||||
|
service=service,
|
||||||
|
endpoints=lambda svc: {
|
||||||
|
"/officeconvertapi.v1.ConversionService/CreateConversion": Endpoint.unary(
|
||||||
|
method=MethodInfo(
|
||||||
|
name="CreateConversion",
|
||||||
|
service_name="officeconvertapi.v1.ConversionService",
|
||||||
|
input=officeconvertapi_dot_v1_dot_conversion__pb2.CreateConversionRequest,
|
||||||
|
output=officeconvertapi_dot_v1_dot_conversion__pb2.CreateConversionResponse,
|
||||||
|
idempotency_level=IdempotencyLevel.UNKNOWN,
|
||||||
|
),
|
||||||
|
function=svc.create_conversion,
|
||||||
|
),
|
||||||
|
"/officeconvertapi.v1.ConversionService/StartConversion": Endpoint.unary(
|
||||||
|
method=MethodInfo(
|
||||||
|
name="StartConversion",
|
||||||
|
service_name="officeconvertapi.v1.ConversionService",
|
||||||
|
input=officeconvertapi_dot_v1_dot_conversion__pb2.StartConversionRequest,
|
||||||
|
output=officeconvertapi_dot_v1_dot_conversion__pb2.StartConversionResponse,
|
||||||
|
idempotency_level=IdempotencyLevel.UNKNOWN,
|
||||||
|
),
|
||||||
|
function=svc.start_conversion,
|
||||||
|
),
|
||||||
|
"/officeconvertapi.v1.ConversionService/GetConversionStatus": Endpoint.unary(
|
||||||
|
method=MethodInfo(
|
||||||
|
name="GetConversionStatus",
|
||||||
|
service_name="officeconvertapi.v1.ConversionService",
|
||||||
|
input=officeconvertapi_dot_v1_dot_conversion__pb2.GetConversionStatusRequest,
|
||||||
|
output=officeconvertapi_dot_v1_dot_conversion__pb2.GetConversionStatusResponse,
|
||||||
|
idempotency_level=IdempotencyLevel.UNKNOWN,
|
||||||
|
),
|
||||||
|
function=svc.get_conversion_status,
|
||||||
|
),
|
||||||
|
"/officeconvertapi.v1.ConversionService/GetSlideDeck": Endpoint.unary(
|
||||||
|
method=MethodInfo(
|
||||||
|
name="GetSlideDeck",
|
||||||
|
service_name="officeconvertapi.v1.ConversionService",
|
||||||
|
input=officeconvertapi_dot_v1_dot_conversion__pb2.GetSlideDeckRequest,
|
||||||
|
output=officeconvertapi_dot_v1_dot_conversion__pb2.GetSlideDeckResponse,
|
||||||
|
idempotency_level=IdempotencyLevel.UNKNOWN,
|
||||||
|
),
|
||||||
|
function=svc.get_slide_deck,
|
||||||
|
),
|
||||||
|
"/officeconvertapi.v1.ConversionService/DeleteConversion": Endpoint.unary(
|
||||||
|
method=MethodInfo(
|
||||||
|
name="DeleteConversion",
|
||||||
|
service_name="officeconvertapi.v1.ConversionService",
|
||||||
|
input=officeconvertapi_dot_v1_dot_conversion__pb2.DeleteConversionRequest,
|
||||||
|
output=officeconvertapi_dot_v1_dot_conversion__pb2.DeleteConversionResponse,
|
||||||
|
idempotency_level=IdempotencyLevel.UNKNOWN,
|
||||||
|
),
|
||||||
|
function=svc.delete_conversion,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
interceptors=interceptors,
|
||||||
|
read_max_bytes=read_max_bytes,
|
||||||
|
compressions=compressions,
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def path(self) -> str:
|
||||||
|
"""Returns the URL path to mount the application to when serving multiple applications."""
|
||||||
|
return "/officeconvertapi.v1.ConversionService"
|
||||||
|
|
||||||
|
|
||||||
|
class ConversionServiceClient(ConnectClient):
|
||||||
|
async def create_conversion(
|
||||||
|
self,
|
||||||
|
request: officeconvertapi_dot_v1_dot_conversion__pb2.CreateConversionRequest,
|
||||||
|
*,
|
||||||
|
headers: Headers | Mapping[str, str] | None = None,
|
||||||
|
timeout_ms: int | None = None,
|
||||||
|
) -> officeconvertapi_dot_v1_dot_conversion__pb2.CreateConversionResponse:
|
||||||
|
return await self.execute_unary(
|
||||||
|
request=request,
|
||||||
|
method=MethodInfo(
|
||||||
|
name="CreateConversion",
|
||||||
|
service_name="officeconvertapi.v1.ConversionService",
|
||||||
|
input=officeconvertapi_dot_v1_dot_conversion__pb2.CreateConversionRequest,
|
||||||
|
output=officeconvertapi_dot_v1_dot_conversion__pb2.CreateConversionResponse,
|
||||||
|
idempotency_level=IdempotencyLevel.UNKNOWN,
|
||||||
|
),
|
||||||
|
headers=headers,
|
||||||
|
timeout_ms=timeout_ms,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def start_conversion(
|
||||||
|
self,
|
||||||
|
request: officeconvertapi_dot_v1_dot_conversion__pb2.StartConversionRequest,
|
||||||
|
*,
|
||||||
|
headers: Headers | Mapping[str, str] | None = None,
|
||||||
|
timeout_ms: int | None = None,
|
||||||
|
) -> officeconvertapi_dot_v1_dot_conversion__pb2.StartConversionResponse:
|
||||||
|
return await self.execute_unary(
|
||||||
|
request=request,
|
||||||
|
method=MethodInfo(
|
||||||
|
name="StartConversion",
|
||||||
|
service_name="officeconvertapi.v1.ConversionService",
|
||||||
|
input=officeconvertapi_dot_v1_dot_conversion__pb2.StartConversionRequest,
|
||||||
|
output=officeconvertapi_dot_v1_dot_conversion__pb2.StartConversionResponse,
|
||||||
|
idempotency_level=IdempotencyLevel.UNKNOWN,
|
||||||
|
),
|
||||||
|
headers=headers,
|
||||||
|
timeout_ms=timeout_ms,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def get_conversion_status(
|
||||||
|
self,
|
||||||
|
request: officeconvertapi_dot_v1_dot_conversion__pb2.GetConversionStatusRequest,
|
||||||
|
*,
|
||||||
|
headers: Headers | Mapping[str, str] | None = None,
|
||||||
|
timeout_ms: int | None = None,
|
||||||
|
) -> officeconvertapi_dot_v1_dot_conversion__pb2.GetConversionStatusResponse:
|
||||||
|
return await self.execute_unary(
|
||||||
|
request=request,
|
||||||
|
method=MethodInfo(
|
||||||
|
name="GetConversionStatus",
|
||||||
|
service_name="officeconvertapi.v1.ConversionService",
|
||||||
|
input=officeconvertapi_dot_v1_dot_conversion__pb2.GetConversionStatusRequest,
|
||||||
|
output=officeconvertapi_dot_v1_dot_conversion__pb2.GetConversionStatusResponse,
|
||||||
|
idempotency_level=IdempotencyLevel.UNKNOWN,
|
||||||
|
),
|
||||||
|
headers=headers,
|
||||||
|
timeout_ms=timeout_ms,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def get_slide_deck(
|
||||||
|
self,
|
||||||
|
request: officeconvertapi_dot_v1_dot_conversion__pb2.GetSlideDeckRequest,
|
||||||
|
*,
|
||||||
|
headers: Headers | Mapping[str, str] | None = None,
|
||||||
|
timeout_ms: int | None = None,
|
||||||
|
) -> officeconvertapi_dot_v1_dot_conversion__pb2.GetSlideDeckResponse:
|
||||||
|
return await self.execute_unary(
|
||||||
|
request=request,
|
||||||
|
method=MethodInfo(
|
||||||
|
name="GetSlideDeck",
|
||||||
|
service_name="officeconvertapi.v1.ConversionService",
|
||||||
|
input=officeconvertapi_dot_v1_dot_conversion__pb2.GetSlideDeckRequest,
|
||||||
|
output=officeconvertapi_dot_v1_dot_conversion__pb2.GetSlideDeckResponse,
|
||||||
|
idempotency_level=IdempotencyLevel.UNKNOWN,
|
||||||
|
),
|
||||||
|
headers=headers,
|
||||||
|
timeout_ms=timeout_ms,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def delete_conversion(
|
||||||
|
self,
|
||||||
|
request: officeconvertapi_dot_v1_dot_conversion__pb2.DeleteConversionRequest,
|
||||||
|
*,
|
||||||
|
headers: Headers | Mapping[str, str] | None = None,
|
||||||
|
timeout_ms: int | None = None,
|
||||||
|
) -> officeconvertapi_dot_v1_dot_conversion__pb2.DeleteConversionResponse:
|
||||||
|
return await self.execute_unary(
|
||||||
|
request=request,
|
||||||
|
method=MethodInfo(
|
||||||
|
name="DeleteConversion",
|
||||||
|
service_name="officeconvertapi.v1.ConversionService",
|
||||||
|
input=officeconvertapi_dot_v1_dot_conversion__pb2.DeleteConversionRequest,
|
||||||
|
output=officeconvertapi_dot_v1_dot_conversion__pb2.DeleteConversionResponse,
|
||||||
|
idempotency_level=IdempotencyLevel.UNKNOWN,
|
||||||
|
),
|
||||||
|
headers=headers,
|
||||||
|
timeout_ms=timeout_ms,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ConversionServiceSync(Protocol):
|
||||||
|
def create_conversion(self, request: officeconvertapi_dot_v1_dot_conversion__pb2.CreateConversionRequest, ctx: RequestContext) -> officeconvertapi_dot_v1_dot_conversion__pb2.CreateConversionResponse:
|
||||||
|
raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
|
||||||
|
def start_conversion(self, request: officeconvertapi_dot_v1_dot_conversion__pb2.StartConversionRequest, ctx: RequestContext) -> officeconvertapi_dot_v1_dot_conversion__pb2.StartConversionResponse:
|
||||||
|
raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
|
||||||
|
def get_conversion_status(self, request: officeconvertapi_dot_v1_dot_conversion__pb2.GetConversionStatusRequest, ctx: RequestContext) -> officeconvertapi_dot_v1_dot_conversion__pb2.GetConversionStatusResponse:
|
||||||
|
raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
|
||||||
|
def get_slide_deck(self, request: officeconvertapi_dot_v1_dot_conversion__pb2.GetSlideDeckRequest, ctx: RequestContext) -> officeconvertapi_dot_v1_dot_conversion__pb2.GetSlideDeckResponse:
|
||||||
|
raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
|
||||||
|
def delete_conversion(self, request: officeconvertapi_dot_v1_dot_conversion__pb2.DeleteConversionRequest, ctx: RequestContext) -> officeconvertapi_dot_v1_dot_conversion__pb2.DeleteConversionResponse:
|
||||||
|
raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
|
||||||
|
|
||||||
|
|
||||||
|
class ConversionServiceWSGIApplication(ConnectWSGIApplication):
|
||||||
|
def __init__(self, service: ConversionServiceSync, interceptors: Iterable[InterceptorSync]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
|
||||||
|
super().__init__(
|
||||||
|
endpoints={
|
||||||
|
"/officeconvertapi.v1.ConversionService/CreateConversion": EndpointSync.unary(
|
||||||
|
method=MethodInfo(
|
||||||
|
name="CreateConversion",
|
||||||
|
service_name="officeconvertapi.v1.ConversionService",
|
||||||
|
input=officeconvertapi_dot_v1_dot_conversion__pb2.CreateConversionRequest,
|
||||||
|
output=officeconvertapi_dot_v1_dot_conversion__pb2.CreateConversionResponse,
|
||||||
|
idempotency_level=IdempotencyLevel.UNKNOWN,
|
||||||
|
),
|
||||||
|
function=service.create_conversion,
|
||||||
|
),
|
||||||
|
"/officeconvertapi.v1.ConversionService/StartConversion": EndpointSync.unary(
|
||||||
|
method=MethodInfo(
|
||||||
|
name="StartConversion",
|
||||||
|
service_name="officeconvertapi.v1.ConversionService",
|
||||||
|
input=officeconvertapi_dot_v1_dot_conversion__pb2.StartConversionRequest,
|
||||||
|
output=officeconvertapi_dot_v1_dot_conversion__pb2.StartConversionResponse,
|
||||||
|
idempotency_level=IdempotencyLevel.UNKNOWN,
|
||||||
|
),
|
||||||
|
function=service.start_conversion,
|
||||||
|
),
|
||||||
|
"/officeconvertapi.v1.ConversionService/GetConversionStatus": EndpointSync.unary(
|
||||||
|
method=MethodInfo(
|
||||||
|
name="GetConversionStatus",
|
||||||
|
service_name="officeconvertapi.v1.ConversionService",
|
||||||
|
input=officeconvertapi_dot_v1_dot_conversion__pb2.GetConversionStatusRequest,
|
||||||
|
output=officeconvertapi_dot_v1_dot_conversion__pb2.GetConversionStatusResponse,
|
||||||
|
idempotency_level=IdempotencyLevel.UNKNOWN,
|
||||||
|
),
|
||||||
|
function=service.get_conversion_status,
|
||||||
|
),
|
||||||
|
"/officeconvertapi.v1.ConversionService/GetSlideDeck": EndpointSync.unary(
|
||||||
|
method=MethodInfo(
|
||||||
|
name="GetSlideDeck",
|
||||||
|
service_name="officeconvertapi.v1.ConversionService",
|
||||||
|
input=officeconvertapi_dot_v1_dot_conversion__pb2.GetSlideDeckRequest,
|
||||||
|
output=officeconvertapi_dot_v1_dot_conversion__pb2.GetSlideDeckResponse,
|
||||||
|
idempotency_level=IdempotencyLevel.UNKNOWN,
|
||||||
|
),
|
||||||
|
function=service.get_slide_deck,
|
||||||
|
),
|
||||||
|
"/officeconvertapi.v1.ConversionService/DeleteConversion": EndpointSync.unary(
|
||||||
|
method=MethodInfo(
|
||||||
|
name="DeleteConversion",
|
||||||
|
service_name="officeconvertapi.v1.ConversionService",
|
||||||
|
input=officeconvertapi_dot_v1_dot_conversion__pb2.DeleteConversionRequest,
|
||||||
|
output=officeconvertapi_dot_v1_dot_conversion__pb2.DeleteConversionResponse,
|
||||||
|
idempotency_level=IdempotencyLevel.UNKNOWN,
|
||||||
|
),
|
||||||
|
function=service.delete_conversion,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
interceptors=interceptors,
|
||||||
|
read_max_bytes=read_max_bytes,
|
||||||
|
compressions=compressions,
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def path(self) -> str:
|
||||||
|
"""Returns the URL path to mount the application to when serving multiple applications."""
|
||||||
|
return "/officeconvertapi.v1.ConversionService"
|
||||||
|
|
||||||
|
|
||||||
|
class ConversionServiceClientSync(ConnectClientSync):
|
||||||
|
def create_conversion(
|
||||||
|
self,
|
||||||
|
request: officeconvertapi_dot_v1_dot_conversion__pb2.CreateConversionRequest,
|
||||||
|
*,
|
||||||
|
headers: Headers | Mapping[str, str] | None = None,
|
||||||
|
timeout_ms: int | None = None,
|
||||||
|
) -> officeconvertapi_dot_v1_dot_conversion__pb2.CreateConversionResponse:
|
||||||
|
return self.execute_unary(
|
||||||
|
request=request,
|
||||||
|
method=MethodInfo(
|
||||||
|
name="CreateConversion",
|
||||||
|
service_name="officeconvertapi.v1.ConversionService",
|
||||||
|
input=officeconvertapi_dot_v1_dot_conversion__pb2.CreateConversionRequest,
|
||||||
|
output=officeconvertapi_dot_v1_dot_conversion__pb2.CreateConversionResponse,
|
||||||
|
idempotency_level=IdempotencyLevel.UNKNOWN,
|
||||||
|
),
|
||||||
|
headers=headers,
|
||||||
|
timeout_ms=timeout_ms,
|
||||||
|
)
|
||||||
|
|
||||||
|
def start_conversion(
|
||||||
|
self,
|
||||||
|
request: officeconvertapi_dot_v1_dot_conversion__pb2.StartConversionRequest,
|
||||||
|
*,
|
||||||
|
headers: Headers | Mapping[str, str] | None = None,
|
||||||
|
timeout_ms: int | None = None,
|
||||||
|
) -> officeconvertapi_dot_v1_dot_conversion__pb2.StartConversionResponse:
|
||||||
|
return self.execute_unary(
|
||||||
|
request=request,
|
||||||
|
method=MethodInfo(
|
||||||
|
name="StartConversion",
|
||||||
|
service_name="officeconvertapi.v1.ConversionService",
|
||||||
|
input=officeconvertapi_dot_v1_dot_conversion__pb2.StartConversionRequest,
|
||||||
|
output=officeconvertapi_dot_v1_dot_conversion__pb2.StartConversionResponse,
|
||||||
|
idempotency_level=IdempotencyLevel.UNKNOWN,
|
||||||
|
),
|
||||||
|
headers=headers,
|
||||||
|
timeout_ms=timeout_ms,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_conversion_status(
|
||||||
|
self,
|
||||||
|
request: officeconvertapi_dot_v1_dot_conversion__pb2.GetConversionStatusRequest,
|
||||||
|
*,
|
||||||
|
headers: Headers | Mapping[str, str] | None = None,
|
||||||
|
timeout_ms: int | None = None,
|
||||||
|
) -> officeconvertapi_dot_v1_dot_conversion__pb2.GetConversionStatusResponse:
|
||||||
|
return self.execute_unary(
|
||||||
|
request=request,
|
||||||
|
method=MethodInfo(
|
||||||
|
name="GetConversionStatus",
|
||||||
|
service_name="officeconvertapi.v1.ConversionService",
|
||||||
|
input=officeconvertapi_dot_v1_dot_conversion__pb2.GetConversionStatusRequest,
|
||||||
|
output=officeconvertapi_dot_v1_dot_conversion__pb2.GetConversionStatusResponse,
|
||||||
|
idempotency_level=IdempotencyLevel.UNKNOWN,
|
||||||
|
),
|
||||||
|
headers=headers,
|
||||||
|
timeout_ms=timeout_ms,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_slide_deck(
|
||||||
|
self,
|
||||||
|
request: officeconvertapi_dot_v1_dot_conversion__pb2.GetSlideDeckRequest,
|
||||||
|
*,
|
||||||
|
headers: Headers | Mapping[str, str] | None = None,
|
||||||
|
timeout_ms: int | None = None,
|
||||||
|
) -> officeconvertapi_dot_v1_dot_conversion__pb2.GetSlideDeckResponse:
|
||||||
|
return self.execute_unary(
|
||||||
|
request=request,
|
||||||
|
method=MethodInfo(
|
||||||
|
name="GetSlideDeck",
|
||||||
|
service_name="officeconvertapi.v1.ConversionService",
|
||||||
|
input=officeconvertapi_dot_v1_dot_conversion__pb2.GetSlideDeckRequest,
|
||||||
|
output=officeconvertapi_dot_v1_dot_conversion__pb2.GetSlideDeckResponse,
|
||||||
|
idempotency_level=IdempotencyLevel.UNKNOWN,
|
||||||
|
),
|
||||||
|
headers=headers,
|
||||||
|
timeout_ms=timeout_ms,
|
||||||
|
)
|
||||||
|
|
||||||
|
def delete_conversion(
|
||||||
|
self,
|
||||||
|
request: officeconvertapi_dot_v1_dot_conversion__pb2.DeleteConversionRequest,
|
||||||
|
*,
|
||||||
|
headers: Headers | Mapping[str, str] | None = None,
|
||||||
|
timeout_ms: int | None = None,
|
||||||
|
) -> officeconvertapi_dot_v1_dot_conversion__pb2.DeleteConversionResponse:
|
||||||
|
return self.execute_unary(
|
||||||
|
request=request,
|
||||||
|
method=MethodInfo(
|
||||||
|
name="DeleteConversion",
|
||||||
|
service_name="officeconvertapi.v1.ConversionService",
|
||||||
|
input=officeconvertapi_dot_v1_dot_conversion__pb2.DeleteConversionRequest,
|
||||||
|
output=officeconvertapi_dot_v1_dot_conversion__pb2.DeleteConversionResponse,
|
||||||
|
idempotency_level=IdempotencyLevel.UNKNOWN,
|
||||||
|
),
|
||||||
|
headers=headers,
|
||||||
|
timeout_ms=timeout_ms,
|
||||||
|
)
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||||
|
# NO CHECKED-IN PROTOBUF GENCODE
|
||||||
|
# source: officeconvertapi/v1/conversion.proto
|
||||||
|
# Protobuf Python Version: 7.34.1
|
||||||
|
"""Generated protocol buffer code."""
|
||||||
|
from google.protobuf import descriptor as _descriptor
|
||||||
|
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||||
|
from google.protobuf import runtime_version as _runtime_version
|
||||||
|
from google.protobuf import symbol_database as _symbol_database
|
||||||
|
from google.protobuf.internal import builder as _builder
|
||||||
|
_runtime_version.ValidateProtobufRuntimeVersion(
|
||||||
|
_runtime_version.Domain.PUBLIC,
|
||||||
|
7,
|
||||||
|
34,
|
||||||
|
1,
|
||||||
|
'',
|
||||||
|
'officeconvertapi/v1/conversion.proto'
|
||||||
|
)
|
||||||
|
# @@protoc_insertion_point(imports)
|
||||||
|
|
||||||
|
_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\"\xc8\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\"B\n\x17\x43reateConversionRequest\x12\'\n\x0fsource_filename\x18\x01 \x01(\tR\x0esourceFilename\"\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\"\xe1\x01\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\":\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\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\x42LZJgithub.com/end/officeconvert/gen/go/officeconvertapi/v1;officeconvertapiv1b\x06proto3')
|
||||||
|
|
||||||
|
_globals = globals()
|
||||||
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||||
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'officeconvertapi.v1.conversion_pb2', _globals)
|
||||||
|
if not _descriptor._USE_C_DESCRIPTORS:
|
||||||
|
_globals['DESCRIPTOR']._loaded_options = None
|
||||||
|
_globals['DESCRIPTOR']._serialized_options = b'ZJgithub.com/end/officeconvert/gen/go/officeconvertapi/v1;officeconvertapiv1'
|
||||||
|
_globals['_CONVERSIONSTATUS']._serialized_start=1483
|
||||||
|
_globals['_CONVERSIONSTATUS']._serialized_end=1661
|
||||||
|
_globals['_SLIDE']._serialized_start=94
|
||||||
|
_globals['_SLIDE']._serialized_end=185
|
||||||
|
_globals['_SLIDEDECK']._serialized_start=188
|
||||||
|
_globals['_SLIDEDECK']._serialized_end=388
|
||||||
|
_globals['_CREATECONVERSIONREQUEST']._serialized_start=390
|
||||||
|
_globals['_CREATECONVERSIONREQUEST']._serialized_end=456
|
||||||
|
_globals['_CREATECONVERSIONRESPONSE']._serialized_start=459
|
||||||
|
_globals['_CREATECONVERSIONRESPONSE']._serialized_end=693
|
||||||
|
_globals['_STARTCONVERSIONREQUEST']._serialized_start=695
|
||||||
|
_globals['_STARTCONVERSIONREQUEST']._serialized_end=756
|
||||||
|
_globals['_STARTCONVERSIONRESPONSE']._serialized_start=758
|
||||||
|
_globals['_STARTCONVERSIONRESPONSE']._serialized_end=883
|
||||||
|
_globals['_GETCONVERSIONSTATUSREQUEST']._serialized_start=885
|
||||||
|
_globals['_GETCONVERSIONSTATUSREQUEST']._serialized_end=950
|
||||||
|
_globals['_GETCONVERSIONSTATUSRESPONSE']._serialized_start=953
|
||||||
|
_globals['_GETCONVERSIONSTATUSRESPONSE']._serialized_end=1178
|
||||||
|
_globals['_GETSLIDEDECKREQUEST']._serialized_start=1180
|
||||||
|
_globals['_GETSLIDEDECKREQUEST']._serialized_end=1238
|
||||||
|
_globals['_GETSLIDEDECKRESPONSE']._serialized_start=1240
|
||||||
|
_globals['_GETSLIDEDECKRESPONSE']._serialized_end=1325
|
||||||
|
_globals['_DELETECONVERSIONREQUEST']._serialized_start=1327
|
||||||
|
_globals['_DELETECONVERSIONREQUEST']._serialized_end=1389
|
||||||
|
_globals['_DELETECONVERSIONRESPONSE']._serialized_start=1391
|
||||||
|
_globals['_DELETECONVERSIONRESPONSE']._serialized_end=1480
|
||||||
|
_globals['_CONVERSIONSERVICE']._serialized_start=1664
|
||||||
|
_globals['_CONVERSIONSERVICE']._serialized_end=2252
|
||||||
|
# @@protoc_insertion_point(module_scope)
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
import datetime
|
||||||
|
|
||||||
|
from google.protobuf import timestamp_pb2 as _timestamp_pb2
|
||||||
|
from google.protobuf.internal import containers as _containers
|
||||||
|
from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
|
||||||
|
from google.protobuf import descriptor as _descriptor
|
||||||
|
from google.protobuf import message as _message
|
||||||
|
from collections.abc import Iterable as _Iterable, Mapping as _Mapping
|
||||||
|
from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union
|
||||||
|
|
||||||
|
DESCRIPTOR: _descriptor.FileDescriptor
|
||||||
|
|
||||||
|
class ConversionStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||||
|
__slots__ = ()
|
||||||
|
CONVERSION_STATUS_UNSPECIFIED: _ClassVar[ConversionStatus]
|
||||||
|
CONVERSION_STATUS_PENDING: _ClassVar[ConversionStatus]
|
||||||
|
CONVERSION_STATUS_RUNNING: _ClassVar[ConversionStatus]
|
||||||
|
CONVERSION_STATUS_SUCCEEDED: _ClassVar[ConversionStatus]
|
||||||
|
CONVERSION_STATUS_FAILED: _ClassVar[ConversionStatus]
|
||||||
|
CONVERSION_STATUS_UNSPECIFIED: ConversionStatus
|
||||||
|
CONVERSION_STATUS_PENDING: ConversionStatus
|
||||||
|
CONVERSION_STATUS_RUNNING: ConversionStatus
|
||||||
|
CONVERSION_STATUS_SUCCEEDED: ConversionStatus
|
||||||
|
CONVERSION_STATUS_FAILED: ConversionStatus
|
||||||
|
|
||||||
|
class Slide(_message.Message):
|
||||||
|
__slots__ = ("index", "notes_plain", "image_url")
|
||||||
|
INDEX_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
NOTES_PLAIN_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
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: ...
|
||||||
|
|
||||||
|
class SlideDeck(_message.Message):
|
||||||
|
__slots__ = ("conversion_id", "source_filename", "slides", "created_at")
|
||||||
|
CONVERSION_ID_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
SOURCE_FILENAME_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
SLIDES_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
CREATED_AT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
conversion_id: str
|
||||||
|
source_filename: str
|
||||||
|
slides: _containers.RepeatedCompositeFieldContainer[Slide]
|
||||||
|
created_at: _timestamp_pb2.Timestamp
|
||||||
|
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]] = ...) -> None: ...
|
||||||
|
|
||||||
|
class CreateConversionRequest(_message.Message):
|
||||||
|
__slots__ = ("source_filename",)
|
||||||
|
SOURCE_FILENAME_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
source_filename: str
|
||||||
|
def __init__(self, source_filename: _Optional[str] = ...) -> None: ...
|
||||||
|
|
||||||
|
class CreateConversionResponse(_message.Message):
|
||||||
|
__slots__ = ("conversion_id", "upload_bucket", "upload_object_key", "upload_url", "expires_at")
|
||||||
|
CONVERSION_ID_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
UPLOAD_BUCKET_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
UPLOAD_OBJECT_KEY_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
UPLOAD_URL_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
EXPIRES_AT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
conversion_id: str
|
||||||
|
upload_bucket: str
|
||||||
|
upload_object_key: str
|
||||||
|
upload_url: str
|
||||||
|
expires_at: _timestamp_pb2.Timestamp
|
||||||
|
def __init__(self, conversion_id: _Optional[str] = ..., upload_bucket: _Optional[str] = ..., upload_object_key: _Optional[str] = ..., upload_url: _Optional[str] = ..., expires_at: _Optional[_Union[datetime.datetime, _timestamp_pb2.Timestamp, _Mapping]] = ...) -> None: ...
|
||||||
|
|
||||||
|
class StartConversionRequest(_message.Message):
|
||||||
|
__slots__ = ("conversion_id",)
|
||||||
|
CONVERSION_ID_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
conversion_id: str
|
||||||
|
def __init__(self, conversion_id: _Optional[str] = ...) -> None: ...
|
||||||
|
|
||||||
|
class StartConversionResponse(_message.Message):
|
||||||
|
__slots__ = ("conversion_id", "status")
|
||||||
|
CONVERSION_ID_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
STATUS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
conversion_id: str
|
||||||
|
status: ConversionStatus
|
||||||
|
def __init__(self, conversion_id: _Optional[str] = ..., status: _Optional[_Union[ConversionStatus, str]] = ...) -> None: ...
|
||||||
|
|
||||||
|
class GetConversionStatusRequest(_message.Message):
|
||||||
|
__slots__ = ("conversion_id",)
|
||||||
|
CONVERSION_ID_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
conversion_id: str
|
||||||
|
def __init__(self, conversion_id: _Optional[str] = ...) -> None: ...
|
||||||
|
|
||||||
|
class GetConversionStatusResponse(_message.Message):
|
||||||
|
__slots__ = ("conversion_id", "status", "error_message", "updated_at")
|
||||||
|
CONVERSION_ID_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
STATUS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
ERROR_MESSAGE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
UPDATED_AT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
conversion_id: str
|
||||||
|
status: ConversionStatus
|
||||||
|
error_message: str
|
||||||
|
updated_at: _timestamp_pb2.Timestamp
|
||||||
|
def __init__(self, conversion_id: _Optional[str] = ..., status: _Optional[_Union[ConversionStatus, str]] = ..., error_message: _Optional[str] = ..., updated_at: _Optional[_Union[datetime.datetime, _timestamp_pb2.Timestamp, _Mapping]] = ...) -> None: ...
|
||||||
|
|
||||||
|
class GetSlideDeckRequest(_message.Message):
|
||||||
|
__slots__ = ("conversion_id",)
|
||||||
|
CONVERSION_ID_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
conversion_id: str
|
||||||
|
def __init__(self, conversion_id: _Optional[str] = ...) -> None: ...
|
||||||
|
|
||||||
|
class GetSlideDeckResponse(_message.Message):
|
||||||
|
__slots__ = ("slide_deck",)
|
||||||
|
SLIDE_DECK_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
slide_deck: SlideDeck
|
||||||
|
def __init__(self, slide_deck: _Optional[_Union[SlideDeck, _Mapping]] = ...) -> None: ...
|
||||||
|
|
||||||
|
class DeleteConversionRequest(_message.Message):
|
||||||
|
__slots__ = ("conversion_id",)
|
||||||
|
CONVERSION_ID_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
conversion_id: str
|
||||||
|
def __init__(self, conversion_id: _Optional[str] = ...) -> None: ...
|
||||||
|
|
||||||
|
class DeleteConversionResponse(_message.Message):
|
||||||
|
__slots__ = ("conversion_id", "deleted")
|
||||||
|
CONVERSION_ID_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
DELETED_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
conversion_id: str
|
||||||
|
deleted: bool
|
||||||
|
def __init__(self, conversion_id: _Optional[str] = ..., deleted: _Optional[bool] = ...) -> None: ...
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
module github.com/end/officeconvert
|
||||||
|
|
||||||
|
go 1.25.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
connectrpc.com/connect v1.19.1
|
||||||
|
google.golang.org/protobuf v1.36.11
|
||||||
|
)
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14=
|
||||||
|
connectrpc.com/connect v1.19.1/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w=
|
||||||
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
|
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package officeconvertapi.v1;
|
||||||
|
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
|
||||||
|
option go_package = "github.com/end/officeconvert/gen/go/officeconvertapi/v1;officeconvertapiv1";
|
||||||
|
|
||||||
|
// ConversionService orchestrates presentation conversion jobs.
|
||||||
|
service ConversionService {
|
||||||
|
// CreateConversion allocates a short-lived session and upload URL for a PPTX.
|
||||||
|
rpc CreateConversion(CreateConversionRequest) returns (CreateConversionResponse) {}
|
||||||
|
|
||||||
|
// StartConversion marks upload completion and starts server-side conversion.
|
||||||
|
rpc StartConversion(StartConversionRequest) returns (StartConversionResponse) {}
|
||||||
|
|
||||||
|
// GetConversionStatus returns state transitions for a conversion session.
|
||||||
|
rpc GetConversionStatus(GetConversionStatusRequest) returns (GetConversionStatusResponse) {}
|
||||||
|
|
||||||
|
// GetSlideDeck fetches the final slide deck data after successful conversion.
|
||||||
|
rpc GetSlideDeck(GetSlideDeckRequest) returns (GetSlideDeckResponse) {}
|
||||||
|
|
||||||
|
// DeleteConversion deletes session resources before automatic expiration.
|
||||||
|
rpc DeleteConversion(DeleteConversionRequest) returns (DeleteConversionResponse) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConversionStatus represents the lifecycle state of a conversion request.
|
||||||
|
enum ConversionStatus {
|
||||||
|
CONVERSION_STATUS_UNSPECIFIED = 0;
|
||||||
|
CONVERSION_STATUS_PENDING = 1;
|
||||||
|
CONVERSION_STATUS_RUNNING = 2;
|
||||||
|
CONVERSION_STATUS_SUCCEEDED = 3;
|
||||||
|
CONVERSION_STATUS_FAILED = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slide contains extracted notes and the rendered image URL for one slide.
|
||||||
|
message Slide {
|
||||||
|
int32 index = 1;
|
||||||
|
string notes_plain = 2;
|
||||||
|
string image_url = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SlideDeck is the final structured conversion artifact.
|
||||||
|
message SlideDeck {
|
||||||
|
string conversion_id = 1;
|
||||||
|
string source_filename = 2;
|
||||||
|
repeated Slide slides = 3;
|
||||||
|
google.protobuf.Timestamp created_at = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateConversionRequest starts a conversion session.
|
||||||
|
message CreateConversionRequest {
|
||||||
|
string source_filename = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateConversionResponse returns upload details for the session.
|
||||||
|
message CreateConversionResponse {
|
||||||
|
string conversion_id = 1;
|
||||||
|
string upload_bucket = 2;
|
||||||
|
string upload_object_key = 3;
|
||||||
|
string upload_url = 4;
|
||||||
|
google.protobuf.Timestamp expires_at = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartConversionRequest requests conversion of an already uploaded PPTX.
|
||||||
|
message StartConversionRequest {
|
||||||
|
string conversion_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartConversionResponse returns the first known status after enqueue.
|
||||||
|
message StartConversionResponse {
|
||||||
|
string conversion_id = 1;
|
||||||
|
ConversionStatus status = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConversionStatusRequest asks for a specific conversion status.
|
||||||
|
message GetConversionStatusRequest {
|
||||||
|
string conversion_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConversionStatusResponse returns current status and optional error info.
|
||||||
|
message GetConversionStatusResponse {
|
||||||
|
string conversion_id = 1;
|
||||||
|
ConversionStatus status = 2;
|
||||||
|
string error_message = 3;
|
||||||
|
google.protobuf.Timestamp updated_at = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSlideDeckRequest fetches a completed deck.
|
||||||
|
message GetSlideDeckRequest {
|
||||||
|
string conversion_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSlideDeckResponse contains the converted slide deck.
|
||||||
|
message GetSlideDeckResponse {
|
||||||
|
SlideDeck slide_deck = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteConversionRequest requests immediate cleanup.
|
||||||
|
message DeleteConversionRequest {
|
||||||
|
string conversion_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteConversionResponse confirms cleanup details.
|
||||||
|
message DeleteConversionResponse {
|
||||||
|
string conversion_id = 1;
|
||||||
|
bool deleted = 2;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
[project]
|
||||||
|
name = "officeconvert"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Core conversion primitives for PPTX to SlideDeck artifacts."
|
||||||
|
readme = "../../../README.md"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = [
|
||||||
|
"python-pptx>=1.0.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["hatchling"]
|
||||||
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
|
[tool.hatch.build.targets.wheel]
|
||||||
|
packages = ["src/officeconvert"]
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
"""Public conversion APIs for the officeconvert Python library."""
|
||||||
|
|
||||||
|
from officeconvert.conversion import (
|
||||||
|
SlideArtifact,
|
||||||
|
SlideDeckResult,
|
||||||
|
convert_pptx_to_pdf,
|
||||||
|
convert_pptx_to_slidedeck,
|
||||||
|
extract_slide_notes,
|
||||||
|
render_pdf_to_images,
|
||||||
|
)
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"SlideArtifact",
|
||||||
|
"SlideDeckResult",
|
||||||
|
"convert_pptx_to_pdf",
|
||||||
|
"convert_pptx_to_slidedeck",
|
||||||
|
"extract_slide_notes",
|
||||||
|
"render_pdf_to_images",
|
||||||
|
]
|
||||||
@@ -0,0 +1,225 @@
|
|||||||
|
"""Conversion utilities for transforming PPTX files into slide image artifacts."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from pathlib import Path
|
||||||
|
import subprocess
|
||||||
|
from typing import Iterable
|
||||||
|
|
||||||
|
from pptx import Presentation
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True, slots=True)
|
||||||
|
class SlideArtifact:
|
||||||
|
"""Represents one converted slide image and its extracted notes."""
|
||||||
|
|
||||||
|
index: int
|
||||||
|
image_path: Path
|
||||||
|
notes_plain: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True, slots=True)
|
||||||
|
class SlideDeckResult:
|
||||||
|
"""Represents all conversion artifacts for a single source presentation."""
|
||||||
|
|
||||||
|
source_filename: str
|
||||||
|
slides: list[SlideArtifact]
|
||||||
|
|
||||||
|
|
||||||
|
def convert_pptx_to_pdf(pptx_path: Path, pdf_path: Path, *, timeout_s: int = 120) -> Path:
|
||||||
|
"""Convert a PPTX file to PDF using headless LibreOffice.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pptx_path: Source `.pptx` path.
|
||||||
|
pdf_path: Destination `.pdf` path.
|
||||||
|
timeout_s: Maximum process runtime in seconds.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The resolved PDF path.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
FileNotFoundError: If the source PPTX does not exist.
|
||||||
|
RuntimeError: If LibreOffice fails or does not create expected output.
|
||||||
|
"""
|
||||||
|
if not pptx_path.exists():
|
||||||
|
raise FileNotFoundError(f"source PPTX does not exist: {pptx_path}")
|
||||||
|
|
||||||
|
output_dir = pdf_path.parent.resolve()
|
||||||
|
output_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
command = [
|
||||||
|
"soffice",
|
||||||
|
"--headless",
|
||||||
|
"--convert-to",
|
||||||
|
"pdf",
|
||||||
|
"--outdir",
|
||||||
|
str(output_dir),
|
||||||
|
str(pptx_path.resolve()),
|
||||||
|
]
|
||||||
|
completed = subprocess.run(
|
||||||
|
command,
|
||||||
|
check=False,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=timeout_s,
|
||||||
|
)
|
||||||
|
if completed.returncode != 0:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"LibreOffice conversion failed: {completed.stderr.strip() or completed.stdout.strip()}"
|
||||||
|
)
|
||||||
|
|
||||||
|
generated_pdf = output_dir / f"{pptx_path.stem}.pdf"
|
||||||
|
if not generated_pdf.exists():
|
||||||
|
raise RuntimeError(f"LibreOffice did not create expected PDF: {generated_pdf}")
|
||||||
|
|
||||||
|
if generated_pdf != pdf_path:
|
||||||
|
generated_pdf.replace(pdf_path)
|
||||||
|
|
||||||
|
return pdf_path.resolve()
|
||||||
|
|
||||||
|
|
||||||
|
def render_pdf_to_images(
|
||||||
|
pdf_path: Path,
|
||||||
|
out_dir: Path,
|
||||||
|
*,
|
||||||
|
dpi: int = 180,
|
||||||
|
image_format: str = "png",
|
||||||
|
timeout_s: int = 120,
|
||||||
|
) -> list[Path]:
|
||||||
|
"""Render each PDF page into an image using Poppler's `pdftoppm`.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pdf_path: Source PDF path.
|
||||||
|
out_dir: Output directory for rendered images.
|
||||||
|
dpi: Target rasterization DPI.
|
||||||
|
image_format: Image format supported by `pdftoppm` (`png`, `jpeg`, ...).
|
||||||
|
timeout_s: Maximum command runtime in seconds.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Ordered list of slide image paths.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
FileNotFoundError: If the PDF path does not exist.
|
||||||
|
RuntimeError: If rasterization fails or no output images are produced.
|
||||||
|
"""
|
||||||
|
if not pdf_path.exists():
|
||||||
|
raise FileNotFoundError(f"source PDF does not exist: {pdf_path}")
|
||||||
|
|
||||||
|
out_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
prefix_path = out_dir / "slide"
|
||||||
|
command = [
|
||||||
|
"pdftoppm",
|
||||||
|
"-r",
|
||||||
|
str(dpi),
|
||||||
|
f"-{image_format}",
|
||||||
|
str(pdf_path.resolve()),
|
||||||
|
str(prefix_path),
|
||||||
|
]
|
||||||
|
completed = subprocess.run(
|
||||||
|
command,
|
||||||
|
check=False,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=timeout_s,
|
||||||
|
)
|
||||||
|
if completed.returncode != 0:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Poppler rasterization failed: {completed.stderr.strip() or completed.stdout.strip()}"
|
||||||
|
)
|
||||||
|
|
||||||
|
images = sorted(out_dir.glob(f"slide-*.{image_format}"))
|
||||||
|
if not images:
|
||||||
|
raise RuntimeError(f"no rendered images found in {out_dir}")
|
||||||
|
return [image.resolve() for image in images]
|
||||||
|
|
||||||
|
|
||||||
|
def extract_slide_notes(pptx_path: Path) -> list[str]:
|
||||||
|
"""Extract plain-text notes for each slide in slide index order.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pptx_path: Source presentation path.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A list of note strings aligned with source slide order.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
FileNotFoundError: If the source PPTX does not exist.
|
||||||
|
"""
|
||||||
|
if not pptx_path.exists():
|
||||||
|
raise FileNotFoundError(f"source PPTX does not exist: {pptx_path}")
|
||||||
|
|
||||||
|
presentation = Presentation(str(pptx_path.resolve()))
|
||||||
|
notes: list[str] = []
|
||||||
|
for slide in presentation.slides:
|
||||||
|
if not slide.has_notes_slide:
|
||||||
|
notes.append("")
|
||||||
|
continue
|
||||||
|
notes.append(_extract_notes_text(slide.notes_slide.shapes))
|
||||||
|
return notes
|
||||||
|
|
||||||
|
|
||||||
|
def convert_pptx_to_slidedeck(
|
||||||
|
pptx_path: Path,
|
||||||
|
work_dir: Path,
|
||||||
|
*,
|
||||||
|
dpi: int = 180,
|
||||||
|
image_format: str = "png",
|
||||||
|
) -> SlideDeckResult:
|
||||||
|
"""Convert a PPTX into rendered images and extracted notes.
|
||||||
|
|
||||||
|
The pipeline performs PPTX->PDF conversion with LibreOffice and then PDF->images
|
||||||
|
rendering with Poppler. Notes are extracted from the original PPTX so text
|
||||||
|
fidelity is preserved independent of rendering output.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pptx_path: Source `.pptx` path.
|
||||||
|
work_dir: Scratch directory for generated outputs.
|
||||||
|
dpi: Rasterization DPI for output slide images.
|
||||||
|
image_format: Output image format accepted by `pdftoppm`.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Fully materialized `SlideDeckResult` with local image paths.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: If rendered page count differs from note count.
|
||||||
|
"""
|
||||||
|
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"
|
||||||
|
|
||||||
|
convert_pptx_to_pdf(pptx_path, pdf_path)
|
||||||
|
image_paths = render_pdf_to_images(
|
||||||
|
pdf_path,
|
||||||
|
image_dir,
|
||||||
|
dpi=dpi,
|
||||||
|
image_format=image_format,
|
||||||
|
)
|
||||||
|
notes = extract_slide_notes(pptx_path)
|
||||||
|
|
||||||
|
if len(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"
|
||||||
|
)
|
||||||
|
|
||||||
|
slides = [
|
||||||
|
SlideArtifact(index=index, image_path=image_path, notes_plain=note)
|
||||||
|
for index, (image_path, note) in enumerate(zip(image_paths, notes), start=1)
|
||||||
|
]
|
||||||
|
return SlideDeckResult(source_filename=pptx_path.name, slides=slides)
|
||||||
|
|
||||||
|
|
||||||
|
def _extract_notes_text(shapes: Iterable[object]) -> str:
|
||||||
|
"""Extract plain text from note shapes while preserving paragraph breaks."""
|
||||||
|
segments: list[str] = []
|
||||||
|
for shape in shapes:
|
||||||
|
text_frame = getattr(shape, "text_frame", None)
|
||||||
|
if text_frame is None:
|
||||||
|
continue
|
||||||
|
# Join paragraph runs because notes often contain formatting splits.
|
||||||
|
text = "\n".join(paragraph.text for paragraph in text_frame.paragraphs).strip()
|
||||||
|
if text:
|
||||||
|
segments.append(text)
|
||||||
|
return "\n\n".join(segments).strip()
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
[project]
|
||||||
|
name = "officeconvert-server"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "ConnectRPC server orchestrating file conversions with MinIO."
|
||||||
|
readme = "../../../README.md"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = [
|
||||||
|
"connectrpc>=0.6.0",
|
||||||
|
"minio>=7.2.18",
|
||||||
|
"officeconvert",
|
||||||
|
"uvicorn>=0.35.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["hatchling"]
|
||||||
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
|
[tool.hatch.build.targets.wheel]
|
||||||
|
packages = ["src/officeconvert_server"]
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
"""Public exports for the officeconvert server package."""
|
||||||
|
|
||||||
|
from officeconvert_server.app import app, create_app
|
||||||
|
from officeconvert_server.config import ServerConfig, load_server_config
|
||||||
|
from officeconvert_server.service import ConversionServiceImpl
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"ServerConfig",
|
||||||
|
"ConversionServiceImpl",
|
||||||
|
"app",
|
||||||
|
"create_app",
|
||||||
|
"load_server_config",
|
||||||
|
]
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
"""ASGI application entrypoint for the officeconvert Connect service."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from officeconvertapi.v1.conversion_connect import ConversionServiceASGIApplication
|
||||||
|
|
||||||
|
from officeconvert_server.config import load_server_config
|
||||||
|
from officeconvert_server.service import ConversionServiceImpl
|
||||||
|
from officeconvert_server.storage import MinIOStore
|
||||||
|
|
||||||
|
|
||||||
|
def create_app() -> ConversionServiceASGIApplication:
|
||||||
|
"""Construct and return the configured Connect ASGI application."""
|
||||||
|
config = load_server_config()
|
||||||
|
store = MinIOStore(
|
||||||
|
endpoint=config.minio_endpoint,
|
||||||
|
access_key=config.minio_access_key,
|
||||||
|
secret_key=config.minio_secret_key,
|
||||||
|
secure=config.minio_secure,
|
||||||
|
public_endpoint=config.minio_public_endpoint,
|
||||||
|
)
|
||||||
|
service = ConversionServiceImpl(config=config, store=store)
|
||||||
|
return ConversionServiceASGIApplication(service)
|
||||||
|
|
||||||
|
|
||||||
|
# Exported ASGI application for `uvicorn officeconvert_server.app:app`.
|
||||||
|
app = create_app()
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
"""Runtime configuration for the officeconvert Connect server."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True, slots=True)
|
||||||
|
class ServerConfig:
|
||||||
|
"""Defines environment-driven settings for server orchestration."""
|
||||||
|
|
||||||
|
minio_endpoint: str
|
||||||
|
minio_access_key: str
|
||||||
|
minio_secret_key: str
|
||||||
|
minio_secure: bool
|
||||||
|
minio_public_endpoint: str
|
||||||
|
minio_session_ttl_seconds: int
|
||||||
|
conversion_cleanup_delay_seconds: int
|
||||||
|
|
||||||
|
|
||||||
|
def load_server_config() -> ServerConfig:
|
||||||
|
"""Load server configuration from environment variables."""
|
||||||
|
return ServerConfig(
|
||||||
|
minio_endpoint=os.getenv("MINIO_ENDPOINT", "localhost:9000"),
|
||||||
|
minio_access_key=os.getenv("MINIO_ACCESS_KEY", "minioadmin"),
|
||||||
|
minio_secret_key=os.getenv("MINIO_SECRET_KEY", "minioadmin"),
|
||||||
|
minio_secure=os.getenv("MINIO_USE_SSL", "false").lower() == "true",
|
||||||
|
minio_public_endpoint=os.getenv("MINIO_PUBLIC_ENDPOINT", "localhost:9000"),
|
||||||
|
minio_session_ttl_seconds=int(os.getenv("MINIO_SESSION_TTL_SECONDS", "3600")),
|
||||||
|
conversion_cleanup_delay_seconds=int(
|
||||||
|
os.getenv("CONVERSION_CLEANUP_DELAY_SECONDS", "3600")
|
||||||
|
),
|
||||||
|
)
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
"""In-memory models representing conversion workflow state."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
def utc_now() -> datetime:
|
||||||
|
"""Return the current UTC timestamp with timezone information."""
|
||||||
|
return datetime.now(tz=timezone.utc)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(slots=True)
|
||||||
|
class ConversionSession:
|
||||||
|
"""Stores mutable state for a single conversion lifecycle."""
|
||||||
|
|
||||||
|
conversion_id: str
|
||||||
|
source_filename: str
|
||||||
|
bucket_name: str
|
||||||
|
upload_object_key: str
|
||||||
|
status: int
|
||||||
|
created_at: datetime = field(default_factory=utc_now)
|
||||||
|
updated_at: datetime = field(default_factory=utc_now)
|
||||||
|
error_message: str = ""
|
||||||
|
slide_deck: Any | None = None
|
||||||
|
work_dir: Path | None = None
|
||||||
|
conversion_task: Any | None = None
|
||||||
|
cleanup_task: Any | None = None
|
||||||
@@ -0,0 +1,269 @@
|
|||||||
|
"""Connect service implementation for conversion request orchestration."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
from datetime import datetime, timedelta, timezone
|
||||||
|
from pathlib import Path
|
||||||
|
import shutil
|
||||||
|
import tempfile
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from connectrpc.code import Code
|
||||||
|
from connectrpc.errors import ConnectError
|
||||||
|
from connectrpc.request import RequestContext
|
||||||
|
from google.protobuf.timestamp_pb2 import Timestamp
|
||||||
|
from officeconvert import SlideArtifact, convert_pptx_to_slidedeck
|
||||||
|
from officeconvertapi.v1 import conversion_connect, conversion_pb2
|
||||||
|
|
||||||
|
from officeconvert_server.config import ServerConfig
|
||||||
|
from officeconvert_server.models import ConversionSession, utc_now
|
||||||
|
from officeconvert_server.storage import MinIOStore
|
||||||
|
|
||||||
|
|
||||||
|
class ConversionServiceImpl(conversion_connect.ConversionService):
|
||||||
|
"""Implements the conversion API with in-memory state and MinIO orchestration."""
|
||||||
|
|
||||||
|
def __init__(self, config: ServerConfig, store: MinIOStore) -> None:
|
||||||
|
"""Initialize service with runtime config and storage adapter."""
|
||||||
|
self._config = config
|
||||||
|
self._store = store
|
||||||
|
self._sessions: dict[str, ConversionSession] = {}
|
||||||
|
self._lock = asyncio.Lock()
|
||||||
|
|
||||||
|
async def create_conversion(
|
||||||
|
self,
|
||||||
|
request: conversion_pb2.CreateConversionRequest,
|
||||||
|
ctx: RequestContext,
|
||||||
|
) -> conversion_pb2.CreateConversionResponse:
|
||||||
|
"""Create a new conversion session and return upload credentials."""
|
||||||
|
del ctx
|
||||||
|
source_filename = request.source_filename.strip()
|
||||||
|
if not source_filename:
|
||||||
|
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")
|
||||||
|
|
||||||
|
conversion_id = str(uuid.uuid4())
|
||||||
|
bucket_name = f"oc-{conversion_id}"
|
||||||
|
upload_key = "input/source.pptx"
|
||||||
|
expires_at = utc_now() + timedelta(seconds=self._config.minio_session_ttl_seconds)
|
||||||
|
|
||||||
|
self._store.ensure_bucket(bucket_name)
|
||||||
|
upload_url = self._store.presigned_put_url(
|
||||||
|
bucket_name,
|
||||||
|
upload_key,
|
||||||
|
ttl_seconds=self._config.minio_session_ttl_seconds,
|
||||||
|
)
|
||||||
|
|
||||||
|
session = ConversionSession(
|
||||||
|
conversion_id=conversion_id,
|
||||||
|
source_filename=source_filename,
|
||||||
|
bucket_name=bucket_name,
|
||||||
|
upload_object_key=upload_key,
|
||||||
|
status=conversion_pb2.CONVERSION_STATUS_PENDING,
|
||||||
|
)
|
||||||
|
async with self._lock:
|
||||||
|
self._sessions[conversion_id] = session
|
||||||
|
|
||||||
|
return conversion_pb2.CreateConversionResponse(
|
||||||
|
conversion_id=conversion_id,
|
||||||
|
upload_bucket=bucket_name,
|
||||||
|
upload_object_key=upload_key,
|
||||||
|
upload_url=upload_url,
|
||||||
|
expires_at=_to_timestamp(expires_at),
|
||||||
|
)
|
||||||
|
|
||||||
|
async def start_conversion(
|
||||||
|
self,
|
||||||
|
request: conversion_pb2.StartConversionRequest,
|
||||||
|
ctx: RequestContext,
|
||||||
|
) -> conversion_pb2.StartConversionResponse:
|
||||||
|
"""Start asynchronous conversion for an already-uploaded session payload."""
|
||||||
|
del ctx
|
||||||
|
session = await self._get_session(request.conversion_id)
|
||||||
|
async with self._lock:
|
||||||
|
if session.status == conversion_pb2.CONVERSION_STATUS_RUNNING:
|
||||||
|
return conversion_pb2.StartConversionResponse(
|
||||||
|
conversion_id=session.conversion_id,
|
||||||
|
status=session.status,
|
||||||
|
)
|
||||||
|
if session.status in (
|
||||||
|
conversion_pb2.CONVERSION_STATUS_FAILED,
|
||||||
|
conversion_pb2.CONVERSION_STATUS_SUCCEEDED,
|
||||||
|
):
|
||||||
|
raise ConnectError(
|
||||||
|
Code.FAILED_PRECONDITION,
|
||||||
|
"conversion has already completed",
|
||||||
|
)
|
||||||
|
|
||||||
|
session.status = conversion_pb2.CONVERSION_STATUS_RUNNING
|
||||||
|
session.updated_at = utc_now()
|
||||||
|
session.conversion_task = asyncio.create_task(self._run_conversion(session))
|
||||||
|
|
||||||
|
return conversion_pb2.StartConversionResponse(
|
||||||
|
conversion_id=session.conversion_id,
|
||||||
|
status=session.status,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def get_conversion_status(
|
||||||
|
self,
|
||||||
|
request: conversion_pb2.GetConversionStatusRequest,
|
||||||
|
ctx: RequestContext,
|
||||||
|
) -> conversion_pb2.GetConversionStatusResponse:
|
||||||
|
"""Return current conversion status and optional error details."""
|
||||||
|
del ctx
|
||||||
|
session = await self._get_session(request.conversion_id)
|
||||||
|
return conversion_pb2.GetConversionStatusResponse(
|
||||||
|
conversion_id=session.conversion_id,
|
||||||
|
status=session.status,
|
||||||
|
error_message=session.error_message,
|
||||||
|
updated_at=_to_timestamp(session.updated_at),
|
||||||
|
)
|
||||||
|
|
||||||
|
async def get_slide_deck(
|
||||||
|
self,
|
||||||
|
request: conversion_pb2.GetSlideDeckRequest,
|
||||||
|
ctx: RequestContext,
|
||||||
|
) -> conversion_pb2.GetSlideDeckResponse:
|
||||||
|
"""Return the finished slide deck once conversion succeeds."""
|
||||||
|
del ctx
|
||||||
|
session = await self._get_session(request.conversion_id)
|
||||||
|
if session.status == conversion_pb2.CONVERSION_STATUS_FAILED:
|
||||||
|
raise ConnectError(Code.FAILED_PRECONDITION, session.error_message)
|
||||||
|
if session.status != conversion_pb2.CONVERSION_STATUS_SUCCEEDED:
|
||||||
|
raise ConnectError(Code.FAILED_PRECONDITION, "conversion is not finished yet")
|
||||||
|
if session.slide_deck is None:
|
||||||
|
raise ConnectError(Code.INTERNAL, "slide deck missing from successful session")
|
||||||
|
|
||||||
|
return conversion_pb2.GetSlideDeckResponse(slide_deck=session.slide_deck)
|
||||||
|
|
||||||
|
async def delete_conversion(
|
||||||
|
self,
|
||||||
|
request: conversion_pb2.DeleteConversionRequest,
|
||||||
|
ctx: RequestContext,
|
||||||
|
) -> conversion_pb2.DeleteConversionResponse:
|
||||||
|
"""Delete a conversion session and associated MinIO/local artifacts."""
|
||||||
|
del ctx
|
||||||
|
async with self._lock:
|
||||||
|
session = self._sessions.pop(request.conversion_id, None)
|
||||||
|
if session is None:
|
||||||
|
return conversion_pb2.DeleteConversionResponse(
|
||||||
|
conversion_id=request.conversion_id,
|
||||||
|
deleted=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
if session.cleanup_task is not None:
|
||||||
|
session.cleanup_task.cancel()
|
||||||
|
if session.conversion_task is not None and not session.conversion_task.done():
|
||||||
|
session.conversion_task.cancel()
|
||||||
|
await self._cleanup_local_artifacts(session)
|
||||||
|
await asyncio.to_thread(self._store.remove_bucket_tree, session.bucket_name)
|
||||||
|
return conversion_pb2.DeleteConversionResponse(
|
||||||
|
conversion_id=session.conversion_id,
|
||||||
|
deleted=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def _run_conversion(self, session: ConversionSession) -> None:
|
||||||
|
"""Execute conversion flow and persist terminal state in memory."""
|
||||||
|
work_dir = Path(
|
||||||
|
tempfile.mkdtemp(prefix=f"officeconvert-{session.conversion_id}-")
|
||||||
|
).resolve()
|
||||||
|
session.work_dir = work_dir
|
||||||
|
source_path = work_dir / "input.pptx"
|
||||||
|
try:
|
||||||
|
await asyncio.to_thread(
|
||||||
|
self._store.fget_object,
|
||||||
|
session.bucket_name,
|
||||||
|
session.upload_object_key,
|
||||||
|
source_path,
|
||||||
|
)
|
||||||
|
result = await asyncio.to_thread(
|
||||||
|
convert_pptx_to_slidedeck,
|
||||||
|
source_path,
|
||||||
|
work_dir,
|
||||||
|
)
|
||||||
|
session.slide_deck = await asyncio.to_thread(
|
||||||
|
self._upload_and_build_slide_deck,
|
||||||
|
session,
|
||||||
|
result.slides,
|
||||||
|
result.source_filename,
|
||||||
|
)
|
||||||
|
session.status = conversion_pb2.CONVERSION_STATUS_SUCCEEDED
|
||||||
|
session.updated_at = utc_now()
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
session.status = conversion_pb2.CONVERSION_STATUS_FAILED
|
||||||
|
session.error_message = "conversion cancelled"
|
||||||
|
session.updated_at = utc_now()
|
||||||
|
raise
|
||||||
|
except Exception as exc:
|
||||||
|
session.status = conversion_pb2.CONVERSION_STATUS_FAILED
|
||||||
|
session.error_message = str(exc)
|
||||||
|
session.updated_at = utc_now()
|
||||||
|
finally:
|
||||||
|
await self._cleanup_local_artifacts(session)
|
||||||
|
session.cleanup_task = asyncio.create_task(self._delayed_cleanup(session))
|
||||||
|
|
||||||
|
def _upload_and_build_slide_deck(
|
||||||
|
self,
|
||||||
|
session: ConversionSession,
|
||||||
|
slides: list[SlideArtifact],
|
||||||
|
source_filename: str,
|
||||||
|
) -> conversion_pb2.SlideDeck:
|
||||||
|
"""Upload generated slide images and construct API response payload."""
|
||||||
|
response_slides: list[conversion_pb2.Slide] = []
|
||||||
|
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(
|
||||||
|
session.bucket_name,
|
||||||
|
object_key,
|
||||||
|
ttl_seconds=self._config.minio_session_ttl_seconds,
|
||||||
|
)
|
||||||
|
response_slides.append(
|
||||||
|
conversion_pb2.Slide(
|
||||||
|
index=slide.index,
|
||||||
|
notes_plain=slide.notes_plain,
|
||||||
|
image_url=image_url,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return conversion_pb2.SlideDeck(
|
||||||
|
conversion_id=session.conversion_id,
|
||||||
|
source_filename=source_filename,
|
||||||
|
slides=response_slides,
|
||||||
|
created_at=_to_timestamp(utc_now()),
|
||||||
|
)
|
||||||
|
|
||||||
|
async def _delayed_cleanup(self, session: ConversionSession) -> None:
|
||||||
|
"""Delete storage resources after the configured session retention period."""
|
||||||
|
try:
|
||||||
|
await asyncio.sleep(self._config.conversion_cleanup_delay_seconds)
|
||||||
|
await asyncio.to_thread(self._store.remove_bucket_tree, session.bucket_name)
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
return
|
||||||
|
finally:
|
||||||
|
async with self._lock:
|
||||||
|
self._sessions.pop(session.conversion_id, None)
|
||||||
|
|
||||||
|
async def _cleanup_local_artifacts(self, session: ConversionSession) -> None:
|
||||||
|
"""Delete temporary local files for a session if they still exist."""
|
||||||
|
if session.work_dir is not None and session.work_dir.exists():
|
||||||
|
await asyncio.to_thread(shutil.rmtree, session.work_dir, True)
|
||||||
|
session.work_dir = None
|
||||||
|
|
||||||
|
async def _get_session(self, conversion_id: str) -> ConversionSession:
|
||||||
|
"""Return an existing session or raise a NOT_FOUND error."""
|
||||||
|
async with self._lock:
|
||||||
|
session = self._sessions.get(conversion_id)
|
||||||
|
if session is None:
|
||||||
|
raise ConnectError(Code.NOT_FOUND, "conversion_id not found")
|
||||||
|
return session
|
||||||
|
|
||||||
|
|
||||||
|
def _to_timestamp(value: datetime) -> Timestamp:
|
||||||
|
"""Convert a timezone-aware datetime to protobuf Timestamp."""
|
||||||
|
normalized = value.astimezone(timezone.utc)
|
||||||
|
proto = Timestamp()
|
||||||
|
proto.FromDatetime(normalized)
|
||||||
|
return proto
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
"""MinIO helper abstraction for upload and artifact lifecycle."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
|
from pathlib import Path
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
from minio import Minio
|
||||||
|
from minio.deleteobjects import DeleteObject
|
||||||
|
from minio.error import S3Error
|
||||||
|
|
||||||
|
|
||||||
|
class MinIOStore:
|
||||||
|
"""Provides typed helper methods around MinIO object storage operations."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
endpoint: str,
|
||||||
|
access_key: str,
|
||||||
|
secret_key: str,
|
||||||
|
secure: bool,
|
||||||
|
public_endpoint: str,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize MinIO clients for internal and public URL generation."""
|
||||||
|
self._client = Minio(
|
||||||
|
endpoint,
|
||||||
|
access_key=access_key,
|
||||||
|
secret_key=secret_key,
|
||||||
|
secure=secure,
|
||||||
|
)
|
||||||
|
self._public_client = Minio(
|
||||||
|
public_endpoint,
|
||||||
|
access_key=access_key,
|
||||||
|
secret_key=secret_key,
|
||||||
|
secure=secure,
|
||||||
|
)
|
||||||
|
|
||||||
|
def ensure_bucket(self, bucket_name: str) -> None:
|
||||||
|
"""Create a bucket if it does not already exist."""
|
||||||
|
if not self._client.bucket_exists(bucket_name):
|
||||||
|
self._client.make_bucket(bucket_name)
|
||||||
|
|
||||||
|
def presigned_put_url(self, bucket_name: str, object_key: str, *, ttl_seconds: int) -> str:
|
||||||
|
"""Generate a presigned PUT URL for a single object upload."""
|
||||||
|
return self._public_client.presigned_put_object(
|
||||||
|
bucket_name,
|
||||||
|
object_key,
|
||||||
|
expires=timedelta(seconds=ttl_seconds),
|
||||||
|
)
|
||||||
|
|
||||||
|
def presigned_get_url(self, bucket_name: str, object_key: str, *, ttl_seconds: int) -> str:
|
||||||
|
"""Generate a presigned GET URL for downloading one object."""
|
||||||
|
return self._public_client.presigned_get_object(
|
||||||
|
bucket_name,
|
||||||
|
object_key,
|
||||||
|
expires=timedelta(seconds=ttl_seconds),
|
||||||
|
)
|
||||||
|
|
||||||
|
def fget_object(self, bucket_name: str, object_key: str, output_path: Path) -> None:
|
||||||
|
"""Download one object from MinIO to a local filesystem path."""
|
||||||
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
self._client.fget_object(bucket_name, object_key, str(output_path))
|
||||||
|
|
||||||
|
def fput_object(self, bucket_name: str, object_key: str, source_path: Path) -> None:
|
||||||
|
"""Upload one local filesystem object to MinIO."""
|
||||||
|
self._client.fput_object(bucket_name, object_key, str(source_path))
|
||||||
|
|
||||||
|
def remove_bucket_tree(self, bucket_name: str) -> None:
|
||||||
|
"""Remove all objects in a bucket and then delete the bucket."""
|
||||||
|
objects = list(self._client.list_objects(bucket_name, recursive=True))
|
||||||
|
if objects:
|
||||||
|
errors = self._client.remove_objects(
|
||||||
|
bucket_name,
|
||||||
|
[DeleteObject(obj.object_name) for obj in objects],
|
||||||
|
)
|
||||||
|
for err in errors:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"failed to delete object {err.object_name}: {err.message}"
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
self._client.remove_bucket(bucket_name)
|
||||||
|
except S3Error as exc:
|
||||||
|
# Concurrent cleanup paths may race to remove the same bucket.
|
||||||
|
if exc.code != "NoSuchBucket":
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def object_key_from_presigned_url(url: str) -> str:
|
||||||
|
"""Extract object key from a presigned URL path for diagnostics."""
|
||||||
|
path = urlparse(url).path
|
||||||
|
path_parts = [part for part in path.split("/") if part]
|
||||||
|
return "/".join(path_parts[1:]) if len(path_parts) >= 2 else ""
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[project]
|
||||||
|
name = "officeconvert-workspace"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Workspace root for officeconvert Python packages."
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
|
||||||
|
[tool.uv.workspace]
|
||||||
|
members = [
|
||||||
|
"packages/officeconvert",
|
||||||
|
"packages/server",
|
||||||
|
]
|
||||||
Reference in New Issue
Block a user