From faa0b233f15973c3ea18f537c46bb77e6187ddd0 Mon Sep 17 00:00:00 2001 From: OpenWaygate <27287694+OpenWaygate@users.noreply.github.com> Date: Sat, 24 Jan 2026 23:16:27 +0800 Subject: [PATCH] :recycle: Reuse struct in caption --- cmd/caption/delete.go | 41 +++------- cmd/caption/download.go | 55 ++++--------- cmd/caption/insert.go | 120 +++++++++++----------------- cmd/caption/list.go | 56 ++++--------- cmd/caption/update.go | 103 +++++++----------------- pkg/caption/caption.go | 152 +++++++++++++++++++++--------------- pkg/caption/caption_test.go | 20 ++--- 7 files changed, 216 insertions(+), 331 deletions(-) diff --git a/cmd/caption/delete.go b/cmd/caption/delete.go index 94a14d1..c221f88 100644 --- a/cmd/caption/delete.go +++ b/cmd/caption/delete.go @@ -6,7 +6,6 @@ package caption import ( "bytes" "context" - "io" "log/slog" "time" @@ -24,12 +23,6 @@ const ( deleteIdsUsage = "IDs of the captions to delete" ) -type deleteIn struct { - Ids []string `json:"ids"` - OnBehalfOf string `json:"onBehalfOf"` - OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"` -} - var deleteInSchema = &jsonschema.Schema{ Type: "object", Required: []string{"ids"}, @@ -38,8 +31,8 @@ var deleteInSchema = &jsonschema.Schema{ Type: "array", Description: deleteIdsUsage, Items: &jsonschema.Schema{Type: "string"}, }, - "onBehalfOf": {Type: "string"}, - "onBehalfOfContentOwner": {Type: "string"}, + "on_behalf_of": {Type: "string"}, + "on_behalf_of_content_owner": {Type: "string"}, }, } @@ -71,12 +64,12 @@ var deleteCmd = &cobra.Command{ Short: deleteShort, Long: deleteLong, Run: func(cmd *cobra.Command, args []string) { - input := &deleteIn{ - Ids: ids, - OnBehalfOf: onBehalfOf, - OnBehalfOfContentOwner: onBehalfOfContentOwner, - } - if err := input.call(cmd.OutOrStdout()); err != nil { + input := caption.NewCation( + caption.WithIDs(ids), + caption.WithOnBehalfOf(onBehalfOf), + caption.WithOnBehalfOfContentOwner(onBehalfOfContentOwner), + ) + if err := input.Delete(cmd.OutOrStdout()); err != nil { _ = cmd.Help() cmd.PrintErrf("Error: %v\n", err) } @@ -84,7 +77,7 @@ var deleteCmd = &cobra.Command{ } func deleteHandler( - ctx context.Context, req *mcp.CallToolRequest, input deleteIn, + ctx context.Context, req *mcp.CallToolRequest, input caption.Caption, ) (*mcp.CallToolResult, any, error) { logger := slog.New( mcp.NewLoggingHandler( @@ -96,23 +89,9 @@ func deleteHandler( ) var writer bytes.Buffer - if err := input.call(&writer); err != nil { + if err := input.Delete(&writer); err != nil { logger.ErrorContext(ctx, err.Error(), "input", input) return nil, nil, err } return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil } - -func (d *deleteIn) call(writer io.Writer, opts ...caption.Option) error { - defaultOpts := []caption.Option{ - caption.WithIDs(d.Ids), - caption.WithOnBehalfOf(d.OnBehalfOf), - caption.WithOnBehalfOfContentOwner(d.OnBehalfOfContentOwner), - caption.WithService(nil), - } - defaultOpts = append(defaultOpts, opts...) - - c := caption.NewCation(defaultOpts...) - - return c.Delete(writer) -} diff --git a/cmd/caption/download.go b/cmd/caption/download.go index a42cdd2..fd527f0 100644 --- a/cmd/caption/download.go +++ b/cmd/caption/download.go @@ -6,7 +6,6 @@ package caption import ( "bytes" "context" - "io" "log/slog" "time" @@ -24,15 +23,6 @@ const ( downloadIdUsage = "ID of the caption to download" ) -type downloadIn struct { - Ids []string `json:"ids"` - File string `json:"file"` - Tfmt string `json:"tfmt"` - Tlang string `json:"tlang"` - OnBehalfOf string `json:"onBehalfOf"` - OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"` -} - var downloadInSchema = &jsonschema.Schema{ Type: "object", Required: []string{"ids", "file"}, @@ -46,9 +36,9 @@ var downloadInSchema = &jsonschema.Schema{ Type: "string", Description: tfmtUsage, Enum: []any{"sbv", "srt", "vtt", ""}, }, - "tlang": {Type: "string", Description: tlangUsage}, - "onBehalfOf": {Type: "string"}, - "onBehalfOfContentOwner": {Type: "string"}, + "tlang": {Type: "string", Description: tlangUsage}, + "on_behalf_of": {Type: "string"}, + "on_behalf_of_content_owner": {Type: "string"}, }, } @@ -86,15 +76,15 @@ var downloadCmd = &cobra.Command{ Short: downloadShort, Long: downloadLong, Run: func(cmd *cobra.Command, args []string) { - input := &downloadIn{ - Ids: ids, - File: file, - Tfmt: tfmt, - Tlang: tlang, - OnBehalfOf: onBehalfOf, - OnBehalfOfContentOwner: onBehalfOfContentOwner, - } - if err := input.call(cmd.OutOrStdout()); err != nil { + input := caption.NewCation( + caption.WithIDs(ids), + caption.WithFile(file), + caption.WithTfmt(tfmt), + caption.WithTlang(tlang), + caption.WithOnBehalfOf(onBehalfOf), + caption.WithOnBehalfOfContentOwner(onBehalfOfContentOwner), + ) + if err := input.Download(cmd.OutOrStdout()); err != nil { _ = cmd.Help() cmd.PrintErrf("Error: %v\n", err) } @@ -102,7 +92,7 @@ var downloadCmd = &cobra.Command{ } func downloadHandler( - ctx context.Context, req *mcp.CallToolRequest, input downloadIn, + ctx context.Context, req *mcp.CallToolRequest, input caption.Caption, ) (*mcp.CallToolResult, any, error) { logger := slog.New( mcp.NewLoggingHandler( @@ -114,26 +104,9 @@ func downloadHandler( ) var writer bytes.Buffer - if err := input.call(&writer); err != nil { + if err := input.Download(&writer); err != nil { logger.ErrorContext(ctx, err.Error(), "input", input) return nil, nil, err } return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil } - -func (d *downloadIn) call(writer io.Writer, opts ...caption.Option) error { - defaultOpts := []caption.Option{ - caption.WithIDs(d.Ids), - caption.WithFile(d.File), - caption.WithTfmt(d.Tfmt), - caption.WithTlang(d.Tlang), - caption.WithOnBehalfOf(d.OnBehalfOf), - caption.WithOnBehalfOfContentOwner(d.OnBehalfOfContentOwner), - caption.WithService(nil), - } - defaultOpts = append(defaultOpts, opts...) - - c := caption.NewCation(defaultOpts...) - - return c.Download(writer) -} diff --git a/cmd/caption/insert.go b/cmd/caption/insert.go index 07ae3f1..291611f 100644 --- a/cmd/caption/insert.go +++ b/cmd/caption/insert.go @@ -7,7 +7,6 @@ import ( "bytes" "context" "encoding/json" - "io" "log/slog" "time" @@ -25,67 +24,49 @@ const ( insertLong = "Insert caption to a video" ) -type insertIn struct { - File string `json:"file"` - AudioTrackType string `json:"audioTrackType"` - IsAutoSynced *bool `json:"isAutoSynced,omitempty"` - IsCC *bool `json:"isCC,omitempty"` - IsDraft *bool `json:"isDraft,omitempty"` - IsEasyReader *bool `json:"isEasyReader,omitempty"` - IsLarge *bool `json:"isLarge,omitempty"` - Language string `json:"language"` - Name string `json:"name"` - TrackKind string `json:"trackKind"` - VideoId string `json:"videoId"` - OnBehalfOf string `json:"onBehalfOf"` - OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"` - Output string `json:"output"` - Jsonpath string `json:"jsonpath"` -} - var insertInSchema = &jsonschema.Schema{ Type: "object", Required: []string{"file", "videoId"}, Properties: map[string]*jsonschema.Schema{ "file": {Type: "string", Description: fileUsage}, - "audioTrackType": { + "audio_track_type": { Type: "string", Description: attUsage, Enum: []any{"unknown", "primary", "commentary", "descriptive"}, Default: json.RawMessage(`"unknown"`), }, - "isAutoSynced": { + "is_auto_synced": { Type: "boolean", Description: iasUsage, Enum: []any{true, false}, }, - "isCC": { + "is_cc": { Type: "boolean", Description: iscUsage, Enum: []any{true, false}, }, - "isDraft": { + "is_draft": { Type: "boolean", Description: isdUsage, Enum: []any{true, false}, }, - "isEasyReader": { + "is_easy_reader": { Type: "boolean", Description: iserUsage, Enum: []any{true, false}, }, - "isLarge": { + "is_large": { Type: "boolean", Description: islUsage, Enum: []any{true, false}, }, "language": {Type: "string", Description: languageUsage}, "name": {Type: "string", Description: nameUsage}, - "trackKind": { + "track_kind": { Type: "string", Description: tkUsage, Enum: []any{"standard", "ASR", "forced"}, Default: json.RawMessage(`"standard"`), }, - "videoId": { + "video_id": { Type: "string", Description: vidUsage, Default: json.RawMessage(`""`), }, - "onBehalfOf": {Type: "string"}, - "onBehalfOfContentOwner": {Type: "string"}, + "on_behalf_of": {Type: "string"}, + "on_behalf_of_content_owner": {Type: "string"}, "output": { Type: "string", Description: pkg.SilentUsage, Enum: []any{"json", "yaml", "silent", ""}, @@ -144,24 +125,41 @@ var insertCmd = &cobra.Command{ Short: insertShort, Long: insertLong, Run: func(cmd *cobra.Command, args []string) { - input := &insertIn{ - File: file, - AudioTrackType: audioTrackType, - IsAutoSynced: isAutoSynced, - IsCC: isCC, - IsDraft: isDraft, - IsEasyReader: isEasyReader, - IsLarge: isLarge, - Language: language, - Name: name, - TrackKind: trackKind, - VideoId: videoId, - OnBehalfOf: onBehalfOf, - OnBehalfOfContentOwner: onBehalfOfContentOwner, - Output: output, - Jsonpath: jsonpath, - } - if err := input.call(cmd.OutOrStdout()); err != nil { + // input := &insertIn{ + // File: file, + // AudioTrackType: audioTrackType, + // IsAutoSynced: isAutoSynced, + // IsCC: isCC, + // IsDraft: isDraft, + // IsEasyReader: isEasyReader, + // IsLarge: isLarge, + // Language: language, + // Name: name, + // TrackKind: trackKind, + // VideoId: videoId, + // OnBehalfOf: onBehalfOf, + // OnBehalfOfContentOwner: onBehalfOfContentOwner, + // Output: output, + // Jsonpath: jsonpath, + // } + input := caption.NewCation( + caption.WithFile(file), + caption.WithAudioTrackType(audioTrackType), + caption.WithIsAutoSynced(isAutoSynced), + caption.WithIsCC(isCC), + caption.WithIsDraft(isDraft), + caption.WithIsEasyReader(isEasyReader), + caption.WithIsLarge(isLarge), + caption.WithLanguage(language), + caption.WithName(name), + caption.WithTrackKind(trackKind), + caption.WithVideoId(videoId), + caption.WithOnBehalfOf(onBehalfOf), + caption.WithOnBehalfOfContentOwner(onBehalfOfContentOwner), + caption.WithOutput(output), + caption.WithJsonpath(jsonpath), + ) + if err := input.Insert(cmd.OutOrStdout()); err != nil { _ = cmd.Help() cmd.PrintErrf("Error: %v\n", err) } @@ -169,7 +167,7 @@ var insertCmd = &cobra.Command{ } func insertHandler( - ctx context.Context, req *mcp.CallToolRequest, input insertIn, + ctx context.Context, req *mcp.CallToolRequest, input caption.Caption, ) (*mcp.CallToolResult, any, error) { logger := slog.New( mcp.NewLoggingHandler( @@ -181,33 +179,9 @@ func insertHandler( ) var writer bytes.Buffer - if err := input.call(&writer); err != nil { + if err := input.Insert(&writer); err != nil { logger.ErrorContext(ctx, err.Error(), "input", input) return nil, nil, err } return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil } - -func (i *insertIn) call(writer io.Writer, opts ...caption.Option) error { - defaultOpts := []caption.Option{ - caption.WithFile(i.File), - caption.WithAudioTrackType(i.AudioTrackType), - caption.WithIsAutoSynced(i.IsAutoSynced), - caption.WithIsCC(i.IsCC), - caption.WithIsDraft(i.IsDraft), - caption.WithIsEasyReader(i.IsEasyReader), - caption.WithIsLarge(i.IsLarge), - caption.WithLanguage(i.Language), - caption.WithName(i.Name), - caption.WithTrackKind(i.TrackKind), - caption.WithOnBehalfOf(i.OnBehalfOf), - caption.WithOnBehalfOfContentOwner(i.OnBehalfOfContentOwner), - caption.WithVideoId(i.VideoId), - caption.WithService(nil), - } - defaultOpts = append(defaultOpts, opts...) - - c := caption.NewCation(defaultOpts...) - - return c.Insert(i.Output, i.Jsonpath, writer) -} diff --git a/cmd/caption/list.go b/cmd/caption/list.go index 32d2d19..512f78e 100644 --- a/cmd/caption/list.go +++ b/cmd/caption/list.go @@ -7,7 +7,6 @@ import ( "bytes" "context" "encoding/json" - "io" "log/slog" "time" @@ -26,16 +25,6 @@ const ( listIdsUsage = "IDs of the captions to list" ) -type listIn struct { - Ids []string `json:"ids"` - VideoId string `json:"videoId"` - OnBehalfOf string `json:"onBehalfOf"` - OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"` - Parts []string `json:"parts"` - Output string `json:"output"` - Jsonpath string `json:"jsonpath"` -} - var listInSchema = &jsonschema.Schema{ Type: "object", Required: []string{}, @@ -44,9 +33,9 @@ var listInSchema = &jsonschema.Schema{ Type: "array", Description: listIdsUsage, Items: &jsonschema.Schema{Type: "string"}, }, - "videoId": {Type: "string", Description: vidUsage}, - "onBehalfOf": {Type: "string"}, - "onBehalfOfContentOwner": {Type: "string"}, + "video_id": {Type: "string", Description: vidUsage}, + "on_behalf_of": {Type: "string"}, + "on_behalf_of_content_owner": {Type: "string"}, "parts": { Type: "array", Description: pkg.PartsUsage, Items: &jsonschema.Schema{Type: "string"}, @@ -93,16 +82,16 @@ var listCmd = &cobra.Command{ Short: listShort, Long: listLong, Run: func(cmd *cobra.Command, args []string) { - input := &listIn{ - Ids: ids, - VideoId: videoId, - OnBehalfOf: onBehalfOf, - OnBehalfOfContentOwner: onBehalfOfContentOwner, - Parts: parts, - Output: output, - Jsonpath: jsonpath, - } - if err := input.call(cmd.OutOrStdout()); err != nil { + input := caption.NewCation( + caption.WithIDs(ids), + caption.WithVideoId(videoId), + caption.WithOnBehalfOf(onBehalfOf), + caption.WithOnBehalfOfContentOwner(onBehalfOfContentOwner), + caption.WithParts(parts), + caption.WithOutput(output), + caption.WithJsonpath(jsonpath), + ) + if err := input.List(cmd.OutOrStdout()); err != nil { _ = cmd.Help() cmd.PrintErrf("Error: %v\n", err) } @@ -110,7 +99,7 @@ var listCmd = &cobra.Command{ } func listHandler( - ctx context.Context, req *mcp.CallToolRequest, input listIn, + ctx context.Context, req *mcp.CallToolRequest, input caption.Caption, ) (*mcp.CallToolResult, any, error) { logger := slog.New( mcp.NewLoggingHandler( @@ -120,24 +109,9 @@ func listHandler( ) var writer bytes.Buffer - if err := input.call(&writer); err != nil { + if err := input.List(&writer); err != nil { logger.ErrorContext(ctx, err.Error(), "input", input) return nil, nil, err } return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil } - -func (l *listIn) call(writer io.Writer, opts ...caption.Option) error { - defaultOpts := []caption.Option{ - caption.WithIDs(l.Ids), - caption.WithVideoId(l.VideoId), - caption.WithOnBehalfOf(l.OnBehalfOf), - caption.WithOnBehalfOfContentOwner(l.OnBehalfOfContentOwner), - caption.WithService(nil), - } - defaultOpts = append(defaultOpts, opts...) - - c := caption.NewCation(defaultOpts...) - - return c.List(l.Parts, l.Output, l.Jsonpath, writer) -} diff --git a/cmd/caption/update.go b/cmd/caption/update.go index 09700f6..cdb4019 100644 --- a/cmd/caption/update.go +++ b/cmd/caption/update.go @@ -7,7 +7,6 @@ import ( "bytes" "context" "encoding/json" - "io" "log/slog" "time" @@ -25,63 +24,45 @@ const ( updateLong = "Update caption of a video" ) -type updateIn struct { - File string `json:"file"` - AudioTrackType string `json:"audioTrackType"` - IsAutoSynced *bool `json:"isAutoSynced,omitempty"` - IsCC *bool `json:"isCC,omitempty"` - IsDraft *bool `json:"isDraft,omitempty"` - IsEasyReader *bool `json:"isEasyReader,omitempty"` - IsLarge *bool `json:"isLarge,omitempty"` - Language string `json:"language"` - Name string `json:"name"` - TrackKind string `json:"trackKind"` - VideoId string `json:"videoId"` - OnBehalfOf string `json:"onBehalfOf"` - OnBehalfOfContentOwner string `json:"onBehalfOfContentOwner"` - Output string `json:"output"` - Jsonpath string `json:"jsonpath"` -} - var updateInSchema = &jsonschema.Schema{ Type: "object", Required: []string{"videoId"}, Properties: map[string]*jsonschema.Schema{ "file": {Type: "string", Description: fileUsage}, - "audioTrackType": { + "audio_track_type": { Type: "string", Description: attUsage, Default: json.RawMessage(`"unknown"`), }, - "isAutoSynced": { + "is_auto_synced": { Type: "boolean", Description: iasUsage, Enum: []any{true, false}, }, - "isCC": { + "is_cc": { Type: "boolean", Description: iscUsage, Enum: []any{true, false}, }, - "isDraft": { + "is_draft": { Type: "boolean", Description: isdUsage, Enum: []any{true, false}, }, - "isEasyReader": { + "is_easy_reader": { Type: "boolean", Description: iserUsage, Enum: []any{true, false}, }, - "isLarge": { + "is_large": { Type: "boolean", Description: islUsage, Enum: []any{true, false}, }, "language": {Type: "string", Description: languageUsage}, "name": {Type: "string", Description: nameUsage}, - "trackKind": { + "track_kind": { Type: "string", Description: tkUsage, Enum: []any{"standard", "ASR", "forced"}, Default: json.RawMessage(`"standard"`), }, - "videoId": {Type: "string", Description: vidUsage}, - "onBehalfOf": {Type: "string"}, - "onBehalfOfContentOwner": {Type: "string"}, + "video_id": {Type: "string", Description: vidUsage}, + "on_behalf_of": {Type: "string"}, + "on_behalf_of_content_owner": {Type: "string"}, "output": { Type: "string", Description: pkg.SilentUsage, Enum: []any{"json", "yaml", "silent", ""}, @@ -139,24 +120,24 @@ var updateCmd = &cobra.Command{ Short: updateShort, Long: updateLong, Run: func(cmd *cobra.Command, args []string) { - input := &updateIn{ - File: file, - AudioTrackType: audioTrackType, - IsAutoSynced: isAutoSynced, - IsCC: isCC, - IsDraft: isDraft, - IsEasyReader: isEasyReader, - IsLarge: isLarge, - Language: language, - Name: name, - TrackKind: trackKind, - VideoId: videoId, - OnBehalfOf: onBehalfOf, - OnBehalfOfContentOwner: onBehalfOfContentOwner, - Output: output, - Jsonpath: jsonpath, - } - if err := input.call(cmd.OutOrStdout()); err != nil { + input := caption.NewCation( + caption.WithFile(file), + caption.WithAudioTrackType(audioTrackType), + caption.WithIsAutoSynced(isAutoSynced), + caption.WithIsCC(isCC), + caption.WithIsDraft(isDraft), + caption.WithIsEasyReader(isEasyReader), + caption.WithIsLarge(isLarge), + caption.WithLanguage(language), + caption.WithName(name), + caption.WithTrackKind(trackKind), + caption.WithVideoId(videoId), + caption.WithOnBehalfOf(onBehalfOf), + caption.WithOnBehalfOfContentOwner(onBehalfOfContentOwner), + caption.WithOutput(output), + caption.WithJsonpath(jsonpath), + ) + if err := input.Update(cmd.OutOrStdout()); err != nil { _ = cmd.Help() cmd.PrintErrf("Error: %v\n", err) } @@ -164,7 +145,7 @@ var updateCmd = &cobra.Command{ } func updateHandler( - ctx context.Context, req *mcp.CallToolRequest, input updateIn, + ctx context.Context, req *mcp.CallToolRequest, input caption.Caption, ) (*mcp.CallToolResult, any, error) { logger := slog.New( mcp.NewLoggingHandler( @@ -176,33 +157,9 @@ func updateHandler( ) var writer bytes.Buffer - if err := input.call(&writer); err != nil { + if err := input.Update(&writer); err != nil { logger.ErrorContext(ctx, err.Error(), "input", input) return nil, nil, err } return &mcp.CallToolResult{Content: []mcp.Content{&mcp.TextContent{Text: writer.String()}}}, nil, nil } - -func (u *updateIn) call(writer io.Writer, opts ...caption.Option) error { - defaultOpts := []caption.Option{ - caption.WithFile(u.File), - caption.WithAudioTrackType(u.AudioTrackType), - caption.WithIsAutoSynced(u.IsAutoSynced), - caption.WithIsCC(u.IsCC), - caption.WithIsDraft(u.IsDraft), - caption.WithIsEasyReader(u.IsEasyReader), - caption.WithIsLarge(u.IsLarge), - caption.WithLanguage(u.Language), - caption.WithName(u.Name), - caption.WithTrackKind(u.TrackKind), - caption.WithOnBehalfOf(u.OnBehalfOf), - caption.WithOnBehalfOfContentOwner(u.OnBehalfOfContentOwner), - caption.WithVideoId(u.VideoId), - caption.WithService(nil), - } - defaultOpts = append(defaultOpts, opts...) - - c := caption.NewCation(defaultOpts...) - - return c.Update(u.Output, u.Jsonpath, writer) -} diff --git a/pkg/caption/caption.go b/pkg/caption/caption.go index c1f1e48..2383bba 100644 --- a/pkg/caption/caption.go +++ b/pkg/caption/caption.go @@ -17,7 +17,6 @@ import ( ) var ( - service *youtube.Service errGetCaption = errors.New("failed to get caption") errUpdateCaption = errors.New("failed to update caption") errDeleteCaption = errors.New("failed to delete caption") @@ -25,7 +24,8 @@ var ( errDownloadCaption = errors.New("failed to download caption") ) -type caption struct { +type Caption struct { + service *youtube.Service IDs []string `yaml:"ids" json:"ids"` File string `yaml:"file" json:"file"` AudioTrackType string `yaml:"audio_track_type" json:"audio_track_type"` @@ -42,29 +42,35 @@ type caption struct { VideoId string `yaml:"video_id" json:"video_id"` Tfmt string `yaml:"tfmt" json:"tfmt"` Tlang string `yaml:"tlang" json:"tlang"` + + Parts []string `yaml:"parts" json:"parts"` + Output string `yaml:"output" json:"output"` + Jsonpath string `yaml:"jsonpath" json:"jsonpath"` } -type Caption[T youtube.Caption] interface { - Get([]string) ([]*T, error) - List([]string, string, string, io.Writer) error - Insert(string, string, io.Writer) error - Update(string, string, io.Writer) error +type ICaption[T youtube.Caption] interface { + Get() ([]*T, error) + List(io.Writer) error + Insert(io.Writer) error + Update(io.Writer) error Delete(io.Writer) error Download(io.Writer) error + preRun() } -type Option func(*caption) +type Option func(*Caption) -func NewCation(opts ...Option) Caption[youtube.Caption] { - c := &caption{} +func NewCation(opts ...Option) ICaption[youtube.Caption] { + c := &Caption{} for _, opt := range opts { opt(c) } return c } -func (c *caption) Get(parts []string) ([]*youtube.Caption, error) { - call := service.Captions.List(parts, c.VideoId) +func (c *Caption) Get() ([]*youtube.Caption, error) { + c.preRun() + call := c.service.Captions.List(c.Parts, c.VideoId) if len(c.IDs) > 0 { call = call.Id(c.IDs...) } @@ -83,19 +89,17 @@ func (c *caption) Get(parts []string) ([]*youtube.Caption, error) { return res.Items, nil } -func (c *caption) List( - parts []string, output string, jsonpath string, writer io.Writer, -) error { - captions, err := c.Get(parts) +func (c *Caption) List(writer io.Writer) error { + captions, err := c.Get() if err != nil { return err } - switch output { + switch c.Output { case "json": - utils.PrintJSON(captions, jsonpath, writer) + utils.PrintJSON(captions, c.Jsonpath, writer) case "yaml": - utils.PrintYAML(captions, jsonpath, writer) + utils.PrintYAML(captions, c.Jsonpath, writer) case "table": tb := table.NewWriter() tb.SetOutputMirror(writer) @@ -114,9 +118,8 @@ func (c *caption) List( return nil } -func (c *caption) Insert( - output string, jsonpath string, writer io.Writer, -) error { +func (c *Caption) Insert(writer io.Writer) error { + c.preRun() file, err := pkg.Root.Open(c.File) if err != nil { return errors.Join(errInsertCaption, err) @@ -140,7 +143,7 @@ func (c *caption) Insert( }, } - call := service.Captions.Insert([]string{"snippet"}, caption).Media(file) + call := c.service.Captions.Insert([]string{"snippet"}, caption).Media(file) if c.OnBehalfOf != "" { call = call.OnBehalfOf(c.OnBehalfOf) } @@ -153,11 +156,11 @@ func (c *caption) Insert( return errors.Join(errInsertCaption, err) } - switch output { + switch c.Output { case "json": - utils.PrintJSON(res, jsonpath, writer) + utils.PrintJSON(res, c.Jsonpath, writer) case "yaml": - utils.PrintYAML(res, jsonpath, writer) + utils.PrintYAML(res, c.Jsonpath, writer) case "silent": default: _, _ = fmt.Fprintf(writer, "Caption inserted: %s\n", res.Id) @@ -165,10 +168,10 @@ func (c *caption) Insert( return nil } -func (c *caption) Update( - output string, jsonpath string, writer io.Writer, -) error { - captions, err := c.Get([]string{"snippet"}) +func (c *Caption) Update(writer io.Writer) error { + c.preRun() + c.Parts = []string{"snippet"} + captions, err := c.Get() if err != nil { return errors.Join(errUpdateCaption, err) } @@ -208,7 +211,7 @@ func (c *caption) Update( caption.Snippet.VideoId = c.VideoId } - call := service.Captions.Update([]string{"snippet"}, caption) + call := c.service.Captions.Update([]string{"snippet"}, caption) if c.File != "" { file, err := pkg.Root.Open(c.File) if err != nil { @@ -231,11 +234,11 @@ func (c *caption) Update( return errors.Join(errUpdateCaption, err) } - switch output { + switch c.Output { case "json": - utils.PrintJSON(res, jsonpath, writer) + utils.PrintJSON(res, c.Jsonpath, writer) case "yaml": - utils.PrintYAML(res, jsonpath, writer) + utils.PrintYAML(res, c.Jsonpath, writer) case "silent": default: _, _ = fmt.Fprintf(writer, "Caption updated: %s\n", res.Id) @@ -243,9 +246,10 @@ func (c *caption) Update( return nil } -func (c *caption) Delete(writer io.Writer) error { +func (c *Caption) Delete(writer io.Writer) error { + c.preRun() for _, id := range c.IDs { - call := service.Captions.Delete(id) + call := c.service.Captions.Delete(id) if c.OnBehalfOf != "" { call = call.OnBehalfOf(c.OnBehalfOf) } @@ -263,8 +267,9 @@ func (c *caption) Delete(writer io.Writer) error { return nil } -func (c *caption) Download(writer io.Writer) error { - call := service.Captions.Download(c.IDs[0]) +func (c *Caption) Download(writer io.Writer) error { + c.preRun() + call := c.service.Captions.Download(c.IDs[0]) if c.Tfmt != "" { call = call.Tfmt(c.Tfmt) } @@ -308,26 +313,35 @@ func (c *caption) Download(writer io.Writer) error { return nil } +func (c *Caption) preRun() { + if c.service == nil { + c.service = auth.NewY2BService( + auth.WithCredential("", pkg.Root.FS()), + auth.WithCacheToken("", pkg.Root.FS()), + ).GetService() + } +} + func WithIDs(ids []string) Option { - return func(c *caption) { + return func(c *Caption) { c.IDs = ids } } func WithFile(file string) Option { - return func(c *caption) { + return func(c *Caption) { c.File = file } } func WithAudioTrackType(audioTrackType string) Option { - return func(c *caption) { + return func(c *Caption) { c.AudioTrackType = audioTrackType } } func WithIsAutoSynced(isAutoSynced *bool) Option { - return func(c *caption) { + return func(c *Caption) { if isAutoSynced != nil { c.IsAutoSynced = isAutoSynced } @@ -335,7 +349,7 @@ func WithIsAutoSynced(isAutoSynced *bool) Option { } func WithIsCC(isCC *bool) Option { - return func(c *caption) { + return func(c *Caption) { if isCC != nil { c.IsCC = isCC } @@ -343,7 +357,7 @@ func WithIsCC(isCC *bool) Option { } func WithIsDraft(isDraft *bool) Option { - return func(c *caption) { + return func(c *Caption) { if isDraft != nil { c.IsDraft = isDraft } @@ -351,7 +365,7 @@ func WithIsDraft(isDraft *bool) Option { } func WithIsEasyReader(isEasyReader *bool) Option { - return func(c *caption) { + return func(c *Caption) { if isEasyReader != nil { c.IsEasyReader = isEasyReader } @@ -359,7 +373,7 @@ func WithIsEasyReader(isEasyReader *bool) Option { } func WithIsLarge(isLarge *bool) Option { - return func(c *caption) { + return func(c *Caption) { if isLarge != nil { c.IsLarge = isLarge } @@ -367,61 +381,73 @@ func WithIsLarge(isLarge *bool) Option { } func WithLanguage(language string) Option { - return func(c *caption) { + return func(c *Caption) { c.Language = language } } func WithName(name string) Option { - return func(c *caption) { + return func(c *Caption) { c.Name = name } } func WithTrackKind(trackKind string) Option { - return func(c *caption) { + return func(c *Caption) { c.TrackKind = trackKind } } func WithOnBehalfOf(onBehalfOf string) Option { - return func(c *caption) { + return func(c *Caption) { c.OnBehalfOf = onBehalfOf } } func WithOnBehalfOfContentOwner(onBehalfOfContentOwner string) Option { - return func(c *caption) { + return func(c *Caption) { c.OnBehalfOfContentOwner = onBehalfOfContentOwner } } func WithVideoId(videoId string) Option { - return func(c *caption) { + return func(c *Caption) { c.VideoId = videoId } } func WithTfmt(tfmt string) Option { - return func(c *caption) { + return func(c *Caption) { c.Tfmt = tfmt } } func WithTlang(tlang string) Option { - return func(c *caption) { + return func(c *Caption) { c.Tlang = tlang } } -func WithService(svc *youtube.Service) Option { - return func(_ *caption) { - if svc == nil { - svc = auth.NewY2BService( - auth.WithCredential("", pkg.Root.FS()), - auth.WithCacheToken("", pkg.Root.FS()), - ).GetService() - } - service = svc +func WithParts(parts []string) Option { + return func(c *Caption) { + c.Parts = parts + } +} + +func WithOutput(output string) Option { + return func(c *Caption) { + c.Output = output + } +} + +func WithJsonpath(jsonpath string) Option { + return func(c *Caption) { + c.Jsonpath = jsonpath + } +} + +func WithService(svc *youtube.Service) Option { + return func(c *Caption) { + c.service = svc } } diff --git a/pkg/caption/caption_test.go b/pkg/caption/caption_test.go index 1f2055f..d6be07f 100644 --- a/pkg/caption/caption_test.go +++ b/pkg/caption/caption_test.go @@ -15,6 +15,7 @@ func TestNewCation(t *testing.T) { opts []Option } + svc := &youtube.Service{} isAutoSyncedTrue := true isAutoSyncedFalse := false isCCTrue := true @@ -29,7 +30,7 @@ func TestNewCation(t *testing.T) { tests := []struct { name string args args - want Caption[youtube.Caption] + want ICaption[youtube.Caption] }{ { name: "with all options", @@ -51,10 +52,11 @@ func TestNewCation(t *testing.T) { WithVideoId("video123"), WithTfmt("srt"), WithTlang("es"), - WithService(&youtube.Service{}), + WithService(svc), }, }, - want: &caption{ + want: &Caption{ + service: svc, IDs: []string{"caption1", "caption2"}, File: "/path/to/file.srt", AudioTrackType: "primary", @@ -78,7 +80,7 @@ func TestNewCation(t *testing.T) { args: args{ opts: []Option{}, }, - want: &caption{}, + want: &Caption{}, }, { name: "with nil boolean options", @@ -91,7 +93,7 @@ func TestNewCation(t *testing.T) { WithIsLarge(nil), }, }, - want: &caption{}, + want: &Caption{}, }, { name: "with false boolean options", @@ -104,7 +106,7 @@ func TestNewCation(t *testing.T) { WithIsLarge(&isLargeFalse), }, }, - want: &caption{ + want: &Caption{ IsAutoSynced: &isAutoSyncedFalse, IsCC: &isCCFalse, IsDraft: &isDraftFalse, @@ -128,7 +130,7 @@ func TestNewCation(t *testing.T) { WithTlang(""), }, }, - want: &caption{ + want: &Caption{ File: "", AudioTrackType: "", Language: "", @@ -151,7 +153,7 @@ func TestNewCation(t *testing.T) { WithIsCC(&isCCTrue), }, }, - want: &caption{ + want: &Caption{ IDs: []string{"caption1"}, Language: "fr", VideoId: "video456", @@ -164,7 +166,7 @@ func TestNewCation(t *testing.T) { t.Run( tt.name, func(t *testing.T) { if got := NewCation(tt.args.opts...); !reflect.DeepEqual(got, tt.want) { - t.Errorf("NewCation() = %v, want %v", got, tt.want) + t.Errorf("%s\nNewCation() = %v\nwant %v", tt.name, got, tt.want) } }, )