From e9d764817f7429bbe1adce1d6ba958bfbbba56af Mon Sep 17 00:00:00 2001 From: Thomas Cravey Date: Sun, 17 Aug 2025 19:13:18 -0500 Subject: [PATCH] feat(link-summ): improve YouTube summaries using oEmbed title/thumbnail hints; keep prompt link-only --- internal/summarizer/openai.go | 55 ++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/internal/summarizer/openai.go b/internal/summarizer/openai.go index 7e7ab01..04c9940 100644 --- a/internal/summarizer/openai.go +++ b/internal/summarizer/openai.go @@ -2,6 +2,7 @@ package summarizer import ( "context" + "encoding/json" "io" "net/http" "net/url" @@ -172,9 +173,42 @@ func (o *OpenAI) SummarizeLink(ctx context.Context, rawURL string) (string, erro client := openai.NewClientWithConfig(cfg) content := "" + title := "" img := "" + + lu, _ := url.Parse(rawURL) + host := strings.ToLower(lu.Host) + isYouTube := host == "www.youtube.com" || host == "youtube.com" || host == "m.youtube.com" || host == "youtu.be" + if isImageURL(rawURL) { img = rawURL + } else if isYouTube { + // YouTube: try oEmbed for title + thumbnail + watchURL := rawURL + if host == "youtu.be" { + id := strings.TrimPrefix(lu.Path, "/") + watchURL = "https://www.youtube.com/watch?v=" + id + } + ctx2, cancel := context.WithTimeout(ctx, o.linkTimeout) + defer cancel() + oembed := "https://www.youtube.com/oembed?format=json&url=" + url.QueryEscape(watchURL) + req, _ := http.NewRequestWithContext(ctx2, http.MethodGet, oembed, nil) + if resp, err := http.DefaultClient.Do(req); err == nil { + func(){ + defer resp.Body.Close() + if resp.StatusCode >= 200 && resp.StatusCode < 300 { + var oem struct{ + Title string `json:"title"` + Thumb string `json:"thumbnail_url"` + } + if err := json.NewDecoder(resp.Body).Decode(&oem); err == nil { + if oem.Title != "" { title = oem.Title } + if oem.Thumb != "" { img = oem.Thumb } + } + } + }() + } + // No robust transcript grab here; rely on model generalization + title } else if o.followLinks { ctx2, cancel := context.WithTimeout(ctx, o.linkTimeout) defer cancel() @@ -192,14 +226,13 @@ func (o *OpenAI) SummarizeLink(ctx context.Context, rawURL string) (string, erro if art, err := readability.FromReader(strings.NewReader(text), base); err == nil { if at := strings.TrimSpace(art.TextContent); at != "" { text = at + if title == "" && strings.TrimSpace(art.Title) != "" { title = strings.TrimSpace(art.Title) } } } } text = strings.ReplaceAll(text, "\r", "") text = strings.TrimSpace(text) - if len(text) > 6000 { - text = text[:6000] - } + if len(text) > 6000 { text = text[:6000] } content = text } }() @@ -213,7 +246,9 @@ func (o *OpenAI) SummarizeLink(ctx context.Context, rawURL string) (string, erro b := strings.Builder{} b.WriteString("URL: ") b.WriteString(rawURL) - b.WriteString("\n\n") + b.WriteString("\n") + if title != "" { b.WriteString("Title: "); b.WriteString(title); b.WriteString("\n") } + b.WriteString("\n") if content != "" { b.WriteString("Extracted content (may be truncated):\n") b.WriteString(content) @@ -239,17 +274,11 @@ func (o *OpenAI) SummarizeLink(ctx context.Context, rawURL string) (string, erro }, MaxCompletionTokens: o.maxTokens, } - if !reasoningLike { - req.Temperature = 0.2 - } + if !reasoningLike { req.Temperature = 0.2 } resp, err := client.CreateChatCompletion(ctx, req) - if err != nil { - return "", err - } - if len(resp.Choices) == 0 { - return "", nil - } + if err != nil { return "", err } + if len(resp.Choices) == 0 { return "", nil } return strings.TrimSpace(resp.Choices[0].Message.Content), nil }