commit a6b7271
delthas
·
2024-08-07 21:55:29 +0000 UTC
parent 5ec99d5
Try opening links as previews based on their content type Instead of their URL path file extension. Additionally, add a specific case for Twitter/X, which has broken embeds at the moment. I can finally have my Twitter image embeds in my terminal :-)
1 files changed,
+60,
-20
M
app.go
M
app.go
+60,
-20
1@@ -4,6 +4,7 @@ import (
2 "crypto/tls"
3 "errors"
4 "fmt"
5+ "html"
6 "image"
7 _ "image/gif"
8 _ "image/jpeg"
9@@ -15,8 +16,8 @@ import (
10 "net/url"
11 "os"
12 "os/exec"
13- "path"
14 "path/filepath"
15+ "regexp"
16 "strings"
17 "sync"
18 "time"
19@@ -901,7 +902,22 @@ func (app *App) handleNickEvent(ev *events.EventClickNick) {
20 }
21 }
22
23+var patternOpenGraphImage = regexp.MustCompile(`<meta property="og:image" content="(.*?)"/>`)
24+
25 func (app *App) fetchImage(link string) (image.Image, error) {
26+ if u, err := url.Parse(link); err == nil {
27+ changed := true
28+ switch u.Host {
29+ case "twitter.com", "x.com":
30+ u.Host = "fixupx.com"
31+ default:
32+ changed = false
33+ }
34+ if changed {
35+ link = u.String()
36+ }
37+ }
38+
39 c := http.Client{
40 Timeout: 6 * time.Second,
41 }
42@@ -912,9 +928,15 @@ func (app *App) fetchImage(link string) (image.Image, error) {
43 if res.StatusCode != http.StatusOK {
44 return nil, fmt.Errorf("unexpected status code: %d", res.StatusCode)
45 }
46- contentType := res.Header.Get("Content-Type")
47+ contentType, _, err := mime.ParseMediaType(res.Header.Get("Content-Type"))
48+ if err != nil {
49+ return nil, fmt.Errorf("unexpected content type: %v", res.Header.Get("Content-Type"))
50+ }
51+ var isHTML bool
52 switch contentType {
53- case "image/gif", "image/jpeg", "image/png":
54+ case "image/gif", "image/jpeg", "image/png": // Actual image, fetch
55+ case "text/html": // Might have an opengraph image, try fetching
56+ isHTML = true
57 default:
58 return nil, fmt.Errorf("unexpected content type: %v", contentType)
59 }
60@@ -922,6 +944,21 @@ func (app *App) fetchImage(link string) (image.Image, error) {
61 if err != nil {
62 return nil, err
63 }
64+ if isHTML {
65+ b, err := io.ReadAll(io.LimitReader(res.Body, 10*1024))
66+ if err != nil {
67+ return nil, fmt.Errorf("unexpected read error: %v", err)
68+ }
69+ m := patternOpenGraphImage.FindSubmatch(b)
70+ if len(m) < 2 {
71+ return nil, fmt.Errorf("image embed not found")
72+ }
73+ imageLink := html.UnescapeString(string(m[1]))
74+ res, err = c.Get(imageLink)
75+ if err != nil {
76+ return nil, err
77+ }
78+ }
79 img, _, err := ui.DecodeImage(res.Body)
80 res.Body.Close()
81 if err != nil {
82@@ -931,31 +968,31 @@ func (app *App) fetchImage(link string) (image.Image, error) {
83 }
84
85 func (app *App) handleLinkEvent(ev *events.EventClickLink) {
86- var open bool
87- if ev.Mouse {
88- open = true
89- if ev.Event.Modifiers != vaxis.ModCtrl {
90- if u, err := url.Parse(ev.Link); err == nil {
91- switch strings.ToLower(path.Ext(u.Path)) {
92- case ".jpg", ".jpeg", ".png", ".gif":
93- open = false
94- }
95- }
96- }
97- }
98- if open {
99+ open := func() {
100 if strings.HasPrefix(ev.Link, "-") {
101 // Avoid injection of parameters.
102 // Sadly xdg-open does not support "--"...
103 return
104 }
105- go func() {
106- cmd := exec.Command("xdg-open", ev.Link)
107- cmd.Run()
108- }()
109+ cmd := exec.Command("xdg-open", ev.Link)
110+ cmd.Run()
111+ }
112+
113+ if ev.Event.Modifiers == vaxis.ModCtrl {
114+ if ev.Mouse {
115+ // Only react to Ctrl+Click when mouse links are enabled.
116+
117+ // Explicit external link open requested with Ctrl+Click:
118+ // just run xdg-open.
119+ go open()
120+ }
121 return
122 }
123
124+ // Regular link open requested:
125+ // Try fetching as an image and displaying a preview;
126+ // fall back to xdg-open if mouse links are enabled.
127+
128 app.imageLoading = true
129 go func() {
130 img, err := app.fetchImage(ev.Link)
131@@ -966,6 +1003,9 @@ func (app *App) handleLinkEvent(ev *events.EventClickLink) {
132 Image: nil,
133 },
134 }
135+ if ev.Mouse {
136+ open()
137+ }
138 } else {
139 app.events <- event{
140 src: "*",