-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathytt.go
119 lines (107 loc) · 3.59 KB
/
ytt.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package main
import (
"context"
"fmt"
"net/url"
"os"
"sort"
"github.com/alecthomas/kong"
)
type YTTCmd struct {
OpenAIAPIKey string `name:"openai-api-key" help:"OpenAI API key" env:"OPENAI_API_KEY" hidden:""`
AnthropicAPIKey string `help:"Anthropic API key" env:"ANTHROPIC_API_KEY" hidden:""`
GroqAPIKey string `help:"Groq API key" env:"GROQ_API_KEY" hidden:""`
GeminiAPIKey string `help:"Gemini API key" env:"GEMINI_API_KEY" hidden:""`
AWSRegion string `help:"AWS Region" env:"AWS_REGION" hidden:""`
AWSAccessKeyID string `help:"AWS Access Key ID" env:"AWS_ACCESS_KEY_ID" hidden:""`
AWSSecretAccessKey string `help:"AWS Secret Access Key ID" env:"AWS_SECRET_ACCESS_KEY" hidden:""`
AWSSessionToken string `help:"AWS Session Token" env:"AWS_SESSION_TOKEN" hidden:""`
Model LLMModel `help:"Model to use" default:"gpt-4o" short:"m"`
VideoURL *url.URL `arg:"" help:"YouTube video URL" short:"u" optional:""`
Output string `help:"Path to output transcript file (default: stdout)" short:"o"`
ListModels bool `help:"List available models" short:"l"`
}
func (cmd *YTTCmd) getLLMClient() (LLMClient, error) {
var provider LLMProvider
config := Config{
OpenAIAPIKey: cmd.OpenAIAPIKey,
AnthropicAPIKey: cmd.AnthropicAPIKey,
GroqAPIKey: cmd.GroqAPIKey,
GeminiAPIKey: cmd.GeminiAPIKey,
AWSRegion: cmd.AWSRegion,
AWSAccessKeyID: cmd.AWSAccessKeyID,
AWSSecretAccessKey: cmd.AWSSecretAccessKey,
AWSSessionToken: cmd.AWSSessionToken,
}
switch cmd.Model {
case GPT4o, GPT4oMini:
if config.OpenAIAPIKey == "" {
return nil, fmt.Errorf("OpenAI API key required for model %s", cmd.Model)
}
provider = OpenAI
case Claude35Haiku, Claude37Sonnet:
if config.AnthropicAPIKey == "" {
return nil, fmt.Errorf("Anthropic API key required for model %s", cmd.Model)
}
provider = Claude
case Llama3370b, Llama318b:
if config.GroqAPIKey == "" {
return nil, fmt.Errorf("Groq API key required for model %s", cmd.Model)
}
provider = Groq
case Gemini2Flash:
if config.GeminiAPIKey == "" {
return nil, fmt.Errorf("Gemini API key required for model %s", cmd.Model)
}
provider = Gemini
case BedrockClaude37Sonnet, BedrockClaude35Haiku:
if config.AWSRegion == "" || config.AWSAccessKeyID == "" || config.AWSSecretAccessKey == "" || config.AWSSessionToken == "" {
return nil, fmt.Errorf("AWS credentials required for model %s", cmd.Model)
}
provider = Bedrock
default:
return nil, fmt.Errorf("unsupported model: %s", cmd.Model)
}
return NewLLMClient(provider, config)
}
func (cmd *YTTCmd) Validate(kctx *kong.Context) error {
if cmd.VideoURL == nil {
return fmt.Errorf("YouTube video URL is required")
}
return nil
}
func (cmd *YTTCmd) Run() error {
if cmd.ListModels {
fmt.Println("Available models:")
models := make([]string, 0, len(modelTokenLimits))
for model := range modelTokenLimits {
models = append(models, string(model))
}
sort.Strings(models)
for _, model := range models {
fmt.Printf(" %s\n", model)
}
return nil
}
client, err := cmd.getLLMClient()
if err != nil {
return err
}
out := os.Stdout
if cmd.Output != "" {
f, err := os.Create(cmd.Output)
if err != nil {
return fmt.Errorf("failed to create output file: %w", err)
}
defer f.Close()
out = f
}
transcriber := NewYouTubeTranscriber(client, cmd.Model)
err = transcriber.Transcribe(context.Background(), cmd.VideoURL.String(),
func(text string, done bool) error {
_, err := fmt.Fprint(out, text)
return err
})
fmt.Println()
return err
}