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 Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· 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 | |||
| 3 | 3 | import ( | |
| 4 | 4 | "context" | |
| 5 | 5 | "fmt" | |
| 6 | + | "net/url" | |
| 6 | 7 | "os" | |
| 7 | 8 | "strings" | |
| 8 | 9 | "sync" | |
| @@ -539,8 +540,11 @@ func (ci *contentInjector) onLoadEnd(wv *WebView) { | |||
| 539 | 540 | wv.RunJavaScript(context.Background(), videoDiagnosticJS) | |
| 540 | 541 | } | |
| 541 | 542 | ||
| 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 | + | } | |
| 544 | 548 | } | |
| 545 | 549 | } | |
| 546 | 550 | ||
| @@ -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( | |||
| 190 | 190 | if display := gdk.DisplayGetDefault(); display != nil { | |
| 191 | 191 | if cb := display.GetClipboard(); cb != nil { | |
| 192 | 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 | 201 | 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() { | |||
| 392 | 392 | } | |
| 393 | 393 | ||
| 394 | 394 | asyncCb := gio.AsyncReadyCallback(func(_, resultPtr, _ uintptr) { | |
| 395 | - | result := &gio.AsyncResultBase{} | |
| 396 | - | result.SetGoPointer(resultPtr) | |
| 395 | + | result := &gio.AsyncResultBase{Ptr: resultPtr} | |
| 397 | 396 | text, err := cb.ReadTextFinish(result) | |
| 398 | 397 | if err != nil || text == "" { | |
| 399 | 398 | 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 | return | 398 | return | ||
M internal/infrastructure/cef/scheme_handler.go +1 -1
| @@ -233,7 +233,7 @@ func (h *dumbSchemeHandler) handleClipboardSet(request purecef.Request) purecef. | |||
| 233 | 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 | 239 | 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 | |||
| 61 | 61 | Msg("cef: failed to start transcoding resource stream") | |
| 62 | 62 | // Let CEF know we failed — Cancel will be called. | |
| 63 | 63 | rh.cancel() | |
| 64 | + | callback.Cont() | |
| 64 | 65 | return | |
| 65 | 66 | } | |
| 66 | 67 | 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 | return | 65 | return | ||
| 65 | } | 66 | } | ||
| 66 | rh.session = session | 67 | rh.session = session | ||
M internal/infrastructure/transcoder/hwprobe.go +5 -2
| @@ -40,8 +40,11 @@ var vaAPIProfile = hwProfile{ | |||
| 40 | 40 | var nvencProfile = hwProfile{ | |
| 41 | 41 | api: "nvenc", | |
| 42 | 42 | 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"}, | |
| 45 | 48 | } | |
| 46 | 49 | ||
| 47 | 50 | // 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() { | |||
| 106 | 106 | ||
| 107 | 107 | // Close sessions outside the lock. | |
| 108 | 108 | for _, s := range snapshot { | |
| 109 | - | s.cancel() | |
| 110 | - | s.pr.Close() | |
| 109 | + | s.Close() | |
| 111 | 110 | } | |
| 112 | 111 | } | |
| @@ -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 | |||
| 128 | 128 | defer func() { | |
| 129 | 129 | if r := recover(); r != nil { | |
| 130 | 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 | 134 | 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) | ||