detached 6c5dd44

fix: address CodeRabbit findings for clipboard bridge commit

- Fix missing callback.Cont() on transcoder start failure (CEF hang)
- Close pipe writer on panic in transcode goroutine (reader hang)
- Tighten Reddit URL matching to hostname check
- Use session.Close() instead of duplicated cancel+close in pool
- Add debug logging for clipboard set operations
- Fix JSON "ok" to boolean, simplify AsyncResultBase init
- Add NVENC encoder filtering comment
commit
6c5dd447e2e483a055996e8d21be4f0989c0a9c2
author
brice · · 2026-03-25 06:15 UTC
committer
brice · · 2026-03-28 04:39 UTC
parent
c3fc1ba
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
8 files · +21 -9
M internal/infrastructure/cef/content_injector.go +6 -2
@@ -3,6 +3,7 @@ package cef
33 import (
44 "context"
55 "fmt"
6+ "net/url"
67 "os"
78 "strings"
89 "sync"
@@ -539,8 +540,11 @@ func (ci *contentInjector) onLoadEnd(wv *WebView) {
539540 wv.RunJavaScript(context.Background(), videoDiagnosticJS)
540541 }
541542
542- if strings.Contains(uri, "reddit.com") {
543- wv.RunJavaScript(context.Background(), redditDirectVideoJS)
543+ if parsed, err := url.Parse(uri); err == nil {
544+ host := parsed.Hostname()
545+ if host == "reddit.com" || strings.HasSuffix(host, ".reddit.com") {
546+ wv.RunJavaScript(context.Background(), redditDirectVideoJS)
547+ }
544548 }
545549 }
546550
@@ -3,6 +3,7 @@ package cef
3 import (3 import (
4 "context"4 "context"
5 "fmt"5 "fmt"
6+ "net/url"
6 "os"7 "os"
7 "strings"8 "strings"
8 "sync"9 "sync"
@@ -539,8 +540,11 @@ func (ci *contentInjector) onLoadEnd(wv *WebView) {
539 wv.RunJavaScript(context.Background(), videoDiagnosticJS)540 wv.RunJavaScript(context.Background(), videoDiagnosticJS)
540 }541 }
541 542
542- if strings.Contains(uri, "reddit.com") {543+ if parsed, err := url.Parse(uri); err == nil {
543- wv.RunJavaScript(context.Background(), redditDirectVideoJS)544+ host := parsed.Hostname()
545+ if host == "reddit.com" || strings.HasSuffix(host, ".reddit.com") {
546+ wv.RunJavaScript(context.Background(), redditDirectVideoJS)
547+ }
544 }548 }
545 }549 }
546 550
M internal/infrastructure/cef/engine_init.go +5 -0
@@ -190,7 +190,12 @@ func wireEngine(
190190 if display := gdk.DisplayGetDefault(); display != nil {
191191 if cb := display.GetClipboard(); cb != nil {
192192 cb.SetText(text)
193+ logger.Debug().Msg("cef: clipboard set via GDK")
194+ } else {
195+ logger.Debug().Msg("cef: clipboard set failed — no GDK clipboard")
193196 }
197+ } else {
198+ logger.Debug().Msg("cef: clipboard set failed — no GDK display")
194199 }
195200 })
196201 glib.IdleAddOnce(&fn, 0)
@@ -190,7 +190,12 @@ func wireEngine(
190 if display := gdk.DisplayGetDefault(); display != nil {190 if display := gdk.DisplayGetDefault(); display != nil {
191 if cb := display.GetClipboard(); cb != nil {191 if cb := display.GetClipboard(); cb != nil {
192 cb.SetText(text)192 cb.SetText(text)
193+ logger.Debug().Msg("cef: clipboard set via GDK")
194+ } else {
195+ logger.Debug().Msg("cef: clipboard set failed — no GDK clipboard")
193 }196 }
197+ } else {
198+ logger.Debug().Msg("cef: clipboard set failed — no GDK display")
194 }199 }
195 })200 })
196 glib.IdleAddOnce(&fn, 0)201 glib.IdleAddOnce(&fn, 0)
M internal/infrastructure/cef/input_bridge.go +1 -2
@@ -392,8 +392,7 @@ func (ib *inputBridge) pasteFromClipboard() {
392392 }
393393
394394 asyncCb := gio.AsyncReadyCallback(func(_, resultPtr, _ uintptr) {
395- result := &gio.AsyncResultBase{}
396- result.SetGoPointer(resultPtr)
395+ result := &gio.AsyncResultBase{Ptr: resultPtr}
397396 text, err := cb.ReadTextFinish(result)
398397 if err != nil || text == "" {
399398 return
@@ -392,8 +392,7 @@ func (ib *inputBridge) pasteFromClipboard() {
392 }392 }
393 393
394 asyncCb := gio.AsyncReadyCallback(func(_, resultPtr, _ uintptr) {394 asyncCb := gio.AsyncReadyCallback(func(_, resultPtr, _ uintptr) {
395- result := &gio.AsyncResultBase{}395+ result := &gio.AsyncResultBase{Ptr: resultPtr}
396- result.SetGoPointer(resultPtr)
397 text, err := cb.ReadTextFinish(result)396 text, err := cb.ReadTextFinish(result)
398 if err != nil || text == "" {397 if err != nil || text == "" {
399 return398 return
M internal/infrastructure/cef/scheme_handler.go +1 -1
@@ -233,7 +233,7 @@ func (h *dumbSchemeHandler) handleClipboardSet(request purecef.Request) purecef.
233233 h.onClipboardSet(payload.Text)
234234 }
235235
236- return h.newJSONResourceHandler(http.StatusOK, map[string]string{"ok": "true"})
236+ return h.newJSONResourceHandler(http.StatusOK, map[string]any{"ok": true})
237237 }
238238
239239 func (h *dumbSchemeHandler) newRedirectResourceHandler(status int, location string) purecef.ResourceHandler {
@@ -233,7 +233,7 @@ func (h *dumbSchemeHandler) handleClipboardSet(request purecef.Request) purecef.
233 h.onClipboardSet(payload.Text)233 h.onClipboardSet(payload.Text)
234 }234 }
235 235
236- return h.newJSONResourceHandler(http.StatusOK, map[string]string{"ok": "true"})236+ return h.newJSONResourceHandler(http.StatusOK, map[string]any{"ok": true})
237 }237 }
238 238
239 func (h *dumbSchemeHandler) newRedirectResourceHandler(status int, location string) purecef.ResourceHandler {239 func (h *dumbSchemeHandler) newRedirectResourceHandler(status int, location string) purecef.ResourceHandler {
M internal/infrastructure/cef/transcoding_handler.go +1 -0
@@ -61,6 +61,7 @@ func (rh *transcodingResourceHandler) Open(_ purecef.Request, handleRequest unsa
6161 Msg("cef: failed to start transcoding resource stream")
6262 // Let CEF know we failed — Cancel will be called.
6363 rh.cancel()
64+ callback.Cont()
6465 return
6566 }
6667 rh.session = session
@@ -61,6 +61,7 @@ func (rh *transcodingResourceHandler) Open(_ purecef.Request, handleRequest unsa
61 Msg("cef: failed to start transcoding resource stream")61 Msg("cef: failed to start transcoding resource stream")
62 // Let CEF know we failed — Cancel will be called.62 // Let CEF know we failed — Cancel will be called.
63 rh.cancel()63 rh.cancel()
64+ callback.Cont()
64 return65 return
65 }66 }
66 rh.session = session67 rh.session = session
M internal/infrastructure/transcoder/hwprobe.go +5 -2
@@ -40,8 +40,11 @@ var vaAPIProfile = hwProfile{
4040 var nvencProfile = hwProfile{
4141 api: "nvenc",
4242 deviceType: ffmpeg.AV_HWDEVICE_TYPE_CUDA,
43- encoders: []string{"av1_nvenc", "hevc_nvenc", "h264_nvenc"},
44- decoders: []string{"h264_cuvid", "hevc_cuvid"},
43+ // hevc_nvenc and h264_nvenc are probed for logging/diagnostic purposes
44+ // but filtered as non-usable by isUsableEncoder (only open codecs like
45+ // AV1 and VP9 are usable in CEF's minimal build).
46+ encoders: []string{"av1_nvenc", "hevc_nvenc", "h264_nvenc"},
47+ decoders: []string{"h264_cuvid", "hevc_cuvid"},
4548 }
4649
4750 // ProbeGPU detects available GPU hardware encoding capabilities.
@@ -40,8 +40,11 @@ var vaAPIProfile = hwProfile{
40 var nvencProfile = hwProfile{40 var nvencProfile = hwProfile{
41 api: "nvenc",41 api: "nvenc",
42 deviceType: ffmpeg.AV_HWDEVICE_TYPE_CUDA,42 deviceType: ffmpeg.AV_HWDEVICE_TYPE_CUDA,
43- encoders: []string{"av1_nvenc", "hevc_nvenc", "h264_nvenc"},43+ // hevc_nvenc and h264_nvenc are probed for logging/diagnostic purposes
44- decoders: []string{"h264_cuvid", "hevc_cuvid"},44+ // but filtered as non-usable by isUsableEncoder (only open codecs like
45+ // AV1 and VP9 are usable in CEF's minimal build).
46+ encoders: []string{"av1_nvenc", "hevc_nvenc", "h264_nvenc"},
47+ decoders: []string{"h264_cuvid", "hevc_cuvid"},
45 }48 }
46 49
47 // ProbeGPU detects available GPU hardware encoding capabilities.50 // ProbeGPU detects available GPU hardware encoding capabilities.
M internal/infrastructure/transcoder/pool.go +1 -2
@@ -106,7 +106,6 @@ func (p *sessionPool) closeAll() {
106106
107107 // Close sessions outside the lock.
108108 for _, s := range snapshot {
109- s.cancel()
110- s.pr.Close()
109+ s.Close()
111110 }
112111 }
@@ -106,7 +106,6 @@ func (p *sessionPool) closeAll() {
106 106
107 // Close sessions outside the lock.107 // Close sessions outside the lock.
108 for _, s := range snapshot {108 for _, s := range snapshot {
109- s.cancel()109+ s.Close()
110- s.pr.Close()
111 }110 }
112 }111 }
M internal/infrastructure/transcoder/transcoder.go +1 -0
@@ -128,6 +128,7 @@ func (t *Transcoder) Start(ctx context.Context, sourceURL string, headers map[st
128128 defer func() {
129129 if r := recover(); r != nil {
130130 t.logger.Error().Str("session_id", id).Interface("panic", r).Msg("transcode session panicked")
131+ pw.CloseWithError(fmt.Errorf("transcode session panicked: %v", r))
131132 }
132133 }()
133134 p.run(sessionCtx)
@@ -128,6 +128,7 @@ func (t *Transcoder) Start(ctx context.Context, sourceURL string, headers map[st
128 defer func() {128 defer func() {
129 if r := recover(); r != nil {129 if r := recover(); r != nil {
130 t.logger.Error().Str("session_id", id).Interface("panic", r).Msg("transcode session panicked")130 t.logger.Error().Str("session_id", id).Interface("panic", r).Msg("transcode session panicked")
131+ pw.CloseWithError(fmt.Errorf("transcode session panicked: %v", r))
131 }132 }
132 }()133 }()
133 p.run(sessionCtx)134 p.run(sessionCtx)

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close