Add Seriatim input module skeleton
This commit is contained in:
54
internal/modules/input/seriatim/adapter.go
Normal file
54
internal/modules/input/seriatim/adapter.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package seriatim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"gitea.maximumdirect.net/eric/notarius/internal/core/source"
|
||||||
|
"gitea.maximumdirect.net/eric/notarius/internal/framework/contracts"
|
||||||
|
"gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline"
|
||||||
|
)
|
||||||
|
|
||||||
|
const Key = "seriatim"
|
||||||
|
|
||||||
|
const (
|
||||||
|
DocumentKind = "transcript"
|
||||||
|
UnitKind = "transcript_segment"
|
||||||
|
Format = "application/vnd.seriatim.minimal+json"
|
||||||
|
)
|
||||||
|
|
||||||
|
var providedCapabilities = []string{
|
||||||
|
"source.transcript",
|
||||||
|
"transcript.speaker",
|
||||||
|
"transcript.timestamps",
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ contracts.InputAdapter = (*Adapter)(nil)
|
||||||
|
|
||||||
|
type Adapter struct{}
|
||||||
|
|
||||||
|
func New() *Adapter {
|
||||||
|
return &Adapter{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Adapter) Key() string {
|
||||||
|
return Key
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Adapter) Parse(ctx context.Context, req contracts.ParseRequest) (*source.SourceDocument, error) {
|
||||||
|
return nil, fmt.Errorf("seriatim input: source document parsing is not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ModuleSpec() pipeline.ModuleSpec {
|
||||||
|
return pipeline.ModuleSpec{
|
||||||
|
Key: Key,
|
||||||
|
Stage: pipeline.StageInput,
|
||||||
|
Provides: append([]string(nil), providedCapabilities...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Register(registry *pipeline.InputAdapterRegistry) error {
|
||||||
|
return registry.RegisterWithSpec(ModuleSpec(), func() (contracts.InputAdapter, error) {
|
||||||
|
return New(), nil
|
||||||
|
})
|
||||||
|
}
|
||||||
28
internal/modules/input/seriatim/metadata.go
Normal file
28
internal/modules/input/seriatim/metadata.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package seriatim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"gitea.maximumdirect.net/eric/notarius/internal/core/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MetadataSpeaker = "speaker"
|
||||||
|
MetadataStart = "start"
|
||||||
|
MetadataEnd = "end"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Speaker(unit source.SourceUnit) (string, bool) {
|
||||||
|
value, ok := unit.Metadata[MetadataSpeaker].(string)
|
||||||
|
return value, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func Start(unit source.SourceUnit) (json.Number, bool) {
|
||||||
|
value, ok := unit.Metadata[MetadataStart].(json.Number)
|
||||||
|
return value, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func End(unit source.SourceUnit) (json.Number, bool) {
|
||||||
|
value, ok := unit.Metadata[MetadataEnd].(json.Number)
|
||||||
|
return value, ok
|
||||||
|
}
|
||||||
29
internal/modules/input/seriatim/model.go
Normal file
29
internal/modules/input/seriatim/model.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package seriatim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
type transcript struct {
|
||||||
|
Metadata map[string]any `json:"metadata"`
|
||||||
|
Segments []segment `json:"segments"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type segment struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Start json.Number `json:"start"`
|
||||||
|
End json.Number `json:"end"`
|
||||||
|
Speaker string `json:"speaker"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeTranscript(raw []byte) (transcript, error) {
|
||||||
|
var out transcript
|
||||||
|
decoder := json.NewDecoder(bytes.NewReader(raw))
|
||||||
|
decoder.UseNumber()
|
||||||
|
if err := decoder.Decode(&out); err != nil {
|
||||||
|
return transcript{}, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
106
internal/modules/input/seriatim/registry_test.go
Normal file
106
internal/modules/input/seriatim/registry_test.go
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
package seriatim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gitea.maximumdirect.net/eric/notarius/internal/core/source"
|
||||||
|
"gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewReturnsAdapterWithKey(t *testing.T) {
|
||||||
|
adapter := New()
|
||||||
|
if adapter == nil {
|
||||||
|
t.Fatal("New() = nil, want adapter")
|
||||||
|
}
|
||||||
|
if adapter.Key() != Key {
|
||||||
|
t.Fatalf("adapter.Key() = %q, want %q", adapter.Key(), Key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestModuleSpec(t *testing.T) {
|
||||||
|
got := ModuleSpec()
|
||||||
|
want := pipeline.ModuleSpec{
|
||||||
|
Key: Key,
|
||||||
|
Stage: pipeline.StageInput,
|
||||||
|
Provides: []string{
|
||||||
|
"source.transcript",
|
||||||
|
"transcript.speaker",
|
||||||
|
"transcript.timestamps",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(got, want) {
|
||||||
|
t.Fatalf("ModuleSpec() = %#v, want %#v", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
got.Provides[0] = "changed"
|
||||||
|
again := ModuleSpec()
|
||||||
|
if !reflect.DeepEqual(again, want) {
|
||||||
|
t.Fatalf("ModuleSpec() after caller mutation = %#v, want %#v", again, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRegisterMakesAdapterBuildable(t *testing.T) {
|
||||||
|
registry := pipeline.NewInputAdapterRegistry()
|
||||||
|
|
||||||
|
if err := Register(registry); err != nil {
|
||||||
|
t.Fatalf("Register() error = %v, want nil", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
adapter, err := registry.Build(Key)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Build() error = %v, want nil", err)
|
||||||
|
}
|
||||||
|
if adapter.Key() != Key {
|
||||||
|
t.Fatalf("adapter.Key() = %q, want %q", adapter.Key(), Key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRegisterStoresModuleSpec(t *testing.T) {
|
||||||
|
registry := pipeline.NewInputAdapterRegistry()
|
||||||
|
|
||||||
|
if err := Register(registry); err != nil {
|
||||||
|
t.Fatalf("Register() error = %v, want nil", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
got, ok := registry.Spec(Key)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Spec() ok = false, want true")
|
||||||
|
}
|
||||||
|
want := ModuleSpec()
|
||||||
|
if !reflect.DeepEqual(got, want) {
|
||||||
|
t.Fatalf("Spec() = %#v, want %#v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRegisterNilRegistryReturnsError(t *testing.T) {
|
||||||
|
err := Register(nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Register(nil) error = nil, want error")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "input adapter registry") {
|
||||||
|
t.Fatalf("Register(nil) error = %q, want registry context", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMetadataHelpers(t *testing.T) {
|
||||||
|
unit := source.SourceUnit{
|
||||||
|
Metadata: map[string]any{
|
||||||
|
MetadataSpeaker: "Narrator",
|
||||||
|
MetadataStart: json.Number("1.25"),
|
||||||
|
MetadataEnd: json.Number("2.5"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if got, ok := Speaker(unit); !ok || got != "Narrator" {
|
||||||
|
t.Fatalf("Speaker() = %q, %v; want Narrator, true", got, ok)
|
||||||
|
}
|
||||||
|
if got, ok := Start(unit); !ok || got != json.Number("1.25") {
|
||||||
|
t.Fatalf("Start() = %q, %v; want 1.25, true", got, ok)
|
||||||
|
}
|
||||||
|
if got, ok := End(unit); !ok || got != json.Number("2.5") {
|
||||||
|
t.Fatalf("End() = %q, %v; want 2.5, true", got, ok)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user