From 31b6b9824ec0681ae6095cd5ea196c6d85e337db Mon Sep 17 00:00:00 2001 From: Symb0x76 <9667434@qq.com> Date: Fri, 10 Oct 2025 20:53:54 +0800 Subject: [PATCH] fix(build): universal compile for CGO --- .vscode/settings.json | 3 +- Makefile | 6 +- internal/chatlog/conf/tui.go | 1 - internal/chatlog/ctx/context.go | 5 +- internal/chatlog/http/route.go | 535 +--------------------- internal/chatlog/http/service.go | 46 -- internal/chatlog/http/static/index.htm | 291 ------------ internal/speech/whispercpp/transcriber.go | 6 - third_party/whisper/lib/libggml.a | Bin 0 -> 69486 bytes 9 files changed, 11 insertions(+), 882 deletions(-) create mode 100644 third_party/whisper/lib/libggml.a diff --git a/.vscode/settings.json b/.vscode/settings.json index 04e5c00..2781998 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -52,7 +52,8 @@ "variant": "cpp" }, "go.toolsEnvVars": { + "CGO_ENABLED": "1", "CGO_CFLAGS": "-I${workspaceFolder}/third_party/whisper/include", - "CGO_LDFLAGS": "-L${workspaceFolder}/third_party/whisper/lib -lwhisper -lggml -lstdc++ -lm" + "CGO_LDFLAGS": "-L${workspaceFolder}/third_party/whisper/lib" } } diff --git a/Makefile b/Makefile index 485b4fa..d59d933 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,8 @@ ifeq ($(VERSION),) VERSION := $(shell git describe --tags --always --dirty="-dev") endif LDFLAGS := -ldflags '-X "github.com/sjzar/chatlog/pkg/version.Version=$(VERSION)" -w -s' +TAG := --tags "fts5" +CGO_FLAG := CGO_ENABLED=1 CGO_CFLAGS="-I./third_party/whisper/include" CGO_LDFLAGS="-L./third_party/whisper/lib" PLATFORMS := \ darwin/amd64 \ @@ -52,7 +54,7 @@ test: build: @echo "Building for current platform..." @$(MKDIR_BIN) - @CGO_ENABLED=1 $(GO) build --tags "fts5" -trimpath $(LDFLAGS) -o bin/$(BINARY_NAME)$(BINARY_SUFFIX) main.go + @$(CGO_FLAG) $(GO) build -trimpath $(LDFLAGS) $(TAG) -o bin/$(BINARY_NAME)$(BINARY_SUFFIX) main.go @echo "Done!" crossbuild: clean @@ -67,7 +69,7 @@ crossbuild: clean [ "$$os" = "windows" ] && output_name=$$output_name.exe; \ echo "Building for $$os/$$arch..."; \ echo "Building for $$output_name..."; \ - @GOOS=$$os GOARCH=$$arch CGO_ENABLED=1 GOARM=$$float $(GO) build --tags "fts5" -trimpath $(LDFLAGS) -o $$output_name main.go ; \ + @GOOS=$$os GOARCH=$$arch GOARM=$$float $(CGO_FLAG) $(GO) build -trimpath $(LDFLAGS) $(TAG) -o $$output_name main.go ; \ if [ "$(ENABLE_UPX)" = "1" ] && echo "$(UPX_PLATFORMS)" | grep -q "$$os/$$arch"; then \ echo "Compressing binary $$output_name..." && upx --best $$output_name; \ fi; \ diff --git a/internal/chatlog/conf/tui.go b/internal/chatlog/conf/tui.go index 993d5ea..2d54fb4 100644 --- a/internal/chatlog/conf/tui.go +++ b/internal/chatlog/conf/tui.go @@ -5,7 +5,6 @@ type TUIConfig struct { LastAccount string `mapstructure:"last_account" json:"last_account"` History []ProcessConfig `mapstructure:"history" json:"history"` Webhook *Webhook `mapstructure:"webhook" json:"webhook"` - Speech *SpeechConfig `mapstructure:"speech" json:"speech"` } var TUIDefaults = map[string]any{} diff --git a/internal/chatlog/ctx/context.go b/internal/chatlog/ctx/context.go index 5183a74..2453a8d 100644 --- a/internal/chatlog/ctx/context.go +++ b/internal/chatlog/ctx/context.go @@ -186,10 +186,7 @@ func (c *Context) GetWebhook() *conf.Webhook { } func (c *Context) GetSpeech() *conf.SpeechConfig { - if c == nil || c.conf == nil { - return nil - } - return c.conf.Speech + return nil } func (c *Context) SetHTTPEnabled(enabled bool) { diff --git a/internal/chatlog/http/route.go b/internal/chatlog/http/route.go index 1d7bf92..4d8a78c 100644 --- a/internal/chatlog/http/route.go +++ b/internal/chatlog/http/route.go @@ -30,459 +30,9 @@ var EFS embed.FS // 统一的 HTML 预览组件片段 var previewHTMLSnippet = ` - -
-
- 预览 - - -
-
-
-` + +
预览
+` func (s *Service) initRouter() { s.initBaseRouter() @@ -527,69 +77,9 @@ func (s *Service) initAPIRouter() { api.GET("/diary", s.handleDiary) api.GET("/dashboard", s.handleDashboard) api.GET("/search", s.handleSearch) - api.POST("/voice/transcribe", s.handleVoiceTranscribe) } } -func (s *Service) handleVoiceTranscribe(c *gin.Context) { - if s.speech == nil { - c.JSON(http.StatusNotImplemented, gin.H{"error": "speech-to-text not enabled"}) - return - } - - var req struct { - Key string `json:"key"` - } - if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request payload", "detail": err.Error()}) - return - } - - key := strings.TrimSpace(req.Key) - if key == "" { - c.JSON(http.StatusBadRequest, gin.H{"error": "voice key is required"}) - return - } - - media, err := s.db.GetMedia("voice", key) - if err != nil { - errors.Err(c, err) - return - } - if media == nil || len(media.Data) == 0 { - c.JSON(http.StatusNotFound, gin.H{"error": "voice media not found"}) - return - } - - result, err := s.speech.TranscribeSilk(c.Request.Context(), media.Data, s.speechOpts) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": "transcription failed", "detail": err.Error()}) - return - } - - segments := make([]gin.H, 0, len(result.Segments)) - for _, seg := range result.Segments { - segments = append(segments, gin.H{ - "id": seg.ID, - "start": seg.Start.Seconds(), - "end": seg.End.Seconds(), - "text": seg.Text, - }) - } - - response := gin.H{ - "text": result.Text, - "language": result.Language, - "duration": result.Duration.Seconds(), - "segments": segments, - } - if s.speechCfg != nil { - response["model"] = strings.TrimSpace(s.speechCfg.Model) - } - - c.JSON(http.StatusOK, response) -} - func (s *Service) initMCPRouter() { s.router.Any("/mcp", func(c *gin.Context) { s.mcpStreamableServer.ServeHTTP(c.Writer, c.Request) }) s.router.Any("/sse", func(c *gin.Context) { s.mcpSSEServer.ServeHTTP(c.Writer, c.Request) }) @@ -1050,7 +540,7 @@ func (s *Service) handleDashboard(c *gin.Context) { Messages: v.MsgCount, Avatar: s.composeAvatarURL(k), }) - added++; + added++ } } } @@ -2192,23 +1682,6 @@ func messageHTMLPlaceholder(m *model.Message) string { anchorText = "[" + escapedFull + "]" } else if left == "文件" && rest != "" { // 文件保留文件名 anchorText = "[文件]" + template.HTMLEscapeString(rest) - } else if strings.HasPrefix(left, "语音") { - anchorText = "[" + template.HTMLEscapeString(left) + "]" - voiceURL := template.HTMLEscapeString(url) - voiceKey := "" - if idx := strings.Index(url, "/voice/"); idx >= 0 { - keyPart := url[idx+len("/voice/"):] - if cut := strings.IndexAny(keyPart, "?#"); cut >= 0 { - keyPart = keyPart[:cut] - } - voiceKey = keyPart - } - escapedKey := template.HTMLEscapeString(voiceKey) - anchor := `` + anchorText + `` - button := `` - transcript := `` - container := `` + anchor + ` ` + button + ` ` + transcript + `` - return container } else { anchorText = "[" + template.HTMLEscapeString(left) + "]" } diff --git a/internal/chatlog/http/service.go b/internal/chatlog/http/service.go index c30e761..5b1354b 100644 --- a/internal/chatlog/http/service.go +++ b/internal/chatlog/http/service.go @@ -3,18 +3,14 @@ package http import ( "context" "net/http" - "strings" "time" "github.com/gin-gonic/gin" "github.com/mark3labs/mcp-go/server" "github.com/rs/zerolog/log" - "github.com/sjzar/chatlog/internal/chatlog/conf" "github.com/sjzar/chatlog/internal/chatlog/database" "github.com/sjzar/chatlog/internal/errors" - "github.com/sjzar/chatlog/internal/speech" - "github.com/sjzar/chatlog/internal/speech/whispercpp" ) type Service struct { @@ -27,17 +23,11 @@ type Service struct { mcpServer *server.MCPServer mcpSSEServer *server.SSEServer mcpStreamableServer *server.StreamableHTTPServer - - speechCfg *conf.SpeechConfig - speechOpts speech.Options - speech speech.Transcriber - speechErr error } type Config interface { GetHTTPAddr() string GetDataDir() string - GetSpeech() *conf.SpeechConfig } func NewService(conf Config, db *database.Service) *Service { @@ -65,7 +55,6 @@ func NewService(conf Config, db *database.Service) *Service { s.initMCPServer() s.initRouter() - s.initSpeech() return s } @@ -115,44 +104,9 @@ func (s *Service) Stop() error { } log.Info().Msg("HTTP server stopped") - s.shutdownSpeech() return nil } func (s *Service) GetRouter() *gin.Engine { return s.router } - -func (s *Service) initSpeech() { - cfg := s.conf.GetSpeech() - if cfg == nil || !cfg.Enabled { - return - } - - modelPath := strings.TrimSpace(cfg.Model) - if modelPath == "" { - log.Warn().Msg("speech config enabled but model path is empty; disabling speech backend") - return - } - - opts := cfg.ToOptions() - transcriber, err := whispercpp.New(whispercpp.Config{ModelPath: modelPath, DefaultOptions: opts}) - if err != nil { - log.Err(err).Str("model", modelPath).Msg("failed to initialize whisper backend; speech disabled") - s.speechErr = err - return - } - - s.speechCfg = cfg - s.speechOpts = opts - s.speech = transcriber - log.Info().Str("model", modelPath).Msg("speech-to-text backend initialized") -} - -func (s *Service) shutdownSpeech() { - if s.speech != nil { - s.speech.Close() - s.speech = nil - log.Info().Msg("speech-to-text backend stopped") - } -} diff --git a/internal/chatlog/http/static/index.htm b/internal/chatlog/http/static/index.htm index 3f18748..f2f6df1 100644 --- a/internal/chatlog/http/static/index.htm +++ b/internal/chatlog/http/static/index.htm @@ -365,93 +365,6 @@ color: var(--error-color); font-weight: bold; } - - .voice-message { - display: inline-flex; - flex-direction: column; - gap: 6px; - align-items: flex-start; - background: rgba(46, 189, 240, 0.08); - padding: 6px 8px; - border-radius: 8px; - margin: 4px 0; - } - - .voice-message .voice-stt-btn { - background-color: #2d333b; - border: 1px solid #4d6277; - color: #d7e2f2; - font-size: 12px; - padding: 4px 10px; - border-radius: 4px; - cursor: pointer; - transition: background 0.2s ease, opacity 0.2s ease; - } - - .voice-message .voice-stt-btn:hover { - background-color: #3a4a5b; - } - - .voice-message .voice-stt-btn[disabled] { - opacity: 0.65; - cursor: wait; - } - - .voice-message .voice-transcript { - font-size: 13px; - color: #2c3e50; - background: rgba(255, 255, 255, 0.7); - padding: 6px 8px; - border-radius: 6px; - line-height: 1.6; - white-space: pre-wrap; - word-break: break-word; - max-width: 600px; - } - - .voice-message .voice-transcript[data-state="idle"] { - display: none; - } - - .voice-message .voice-text { - white-space: pre-wrap; - word-break: break-word; - } - - .voice-message .voice-transcript .voice-segment { - padding: 4px 0; - border-left: 2px solid rgba(52, 152, 219, 0.45); - margin-top: 4px; - padding-left: 8px; - } - - .voice-message .voice-transcript .voice-segment-time { - font-size: 12px; - color: #6c7a89; - } - - .voice-message .voice-segments { - display: flex; - flex-direction: column; - gap: 6px; - margin-top: 8px; - } - - .voice-message .voice-meta, - .voice-message .voice-transcript .voice-meta { - margin-top: 6px; - font-size: 12px; - color: #6c7a89; - } - - .voice-message .voice-transcript .voice-segment span { - display: block; - } - - .voice-message .voice-text.voice-empty { - color: #95a5a6; - font-style: italic; - } @@ -1261,7 +1174,6 @@ resultContainer.innerHTML = `
${highlighted}
`; } } - prepareVoiceMessages(resultContainer); } catch (error) { resultContainer.innerHTML = ""; errorMessage.textContent = `查询出错: ${error.message}`; @@ -1376,209 +1288,6 @@ }); } - const voiceFallbackCache = new Map(); - - function prepareVoiceMessages(root) { - if (!root) return; - if ( - window.ChatlogSpeech && - typeof window.ChatlogSpeech.refreshVoiceMessages === - "function" - ) { - window.ChatlogSpeech.refreshVoiceMessages(); - return; - } - fallbackEnsureVoiceState(root); - } - - function fallbackEnsureVoiceState(root) { - root.querySelectorAll(".voice-message").forEach((container) => { - const transcript = - container.querySelector(".voice-transcript"); - if (transcript && !transcript.dataset.state) { - transcript.dataset.state = "idle"; - } - const btn = container.querySelector(".voice-stt-btn"); - if (btn && !btn.dataset.labelIdle) { - btn.dataset.labelIdle = btn.textContent.trim(); - } - }); - } - - function voiceKeyFromButton(btn) { - const container = btn.closest(".voice-message"); - if (!container) return ""; - const link = container.querySelector( - "a.voice-original, a[href]" - ); - if (!link) return ""; - const href = link.getAttribute("href") || ""; - try { - const url = new URL(href, window.location.href); - return ( - url.searchParams.get("msgid") || - url.searchParams.get("id") || - url.pathname - ); - } catch (_err) { - return href; - } - } - - async function fallbackVoiceRequest(btn) { - const container = btn.closest(".voice-message"); - const transcript = - container?.querySelector(".voice-transcript"); - const key = voiceKeyFromButton(btn); - if (!container || !transcript || !key) return; - - if (!transcript.dataset.state) { - transcript.dataset.state = "idle"; - } - - const cached = voiceFallbackCache.get(key); - if (cached) { - transcript.innerHTML = renderTranscript(cached); - transcript.dataset.state = "ready"; - btn.disabled = false; - btn.dataset.state = "done"; - btn.textContent = "查看文字"; - return; - } - - const originalText = - btn.dataset.labelIdle || btn.textContent || "语音转文字"; - btn.dataset.labelIdle = originalText; - btn.disabled = true; - btn.dataset.state = "loading"; - btn.textContent = "转文字中…"; - - transcript.dataset.state = "loading"; - transcript.textContent = "语音识别进行中…"; - - try { - const response = await fetch("/api/v1/voice/transcribe", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ key }), - }); - if (!response.ok) { - throw new Error(`HTTP ${response.status}`); - } - const payload = await response.json(); - voiceFallbackCache.set(key, payload); - transcript.innerHTML = renderTranscript(payload); - transcript.dataset.state = "ready"; - btn.disabled = false; - btn.textContent = "已转文字"; - btn.dataset.state = "done"; - } catch (err) { - console.error("语音识别失败:", err); - transcript.dataset.state = "error"; - transcript.textContent = `转文字失败: ${err.message}`; - btn.textContent = "重试转文字"; - btn.dataset.state = "error"; - } finally { - btn.disabled = false; - } - } - - function renderTranscript(data) { - const pieces = []; - const rawText = - data && typeof data.text === "string" - ? data.text.trim() - : ""; - if (rawText) { - pieces.push( - `
${escapeHtml(rawText)}
` - ); - } else { - pieces.push( - '
未识别到文本
' - ); - } - - const segments = Array.isArray(data && data.segments) - ? data.segments - : []; - if (segments.length) { - const rendered = segments - .map((segment) => { - const start = formatSeconds( - segment && segment.start - ); - const end = formatSeconds(segment && segment.end); - const caption = - segment && segment.text - ? segment.text.trim() - : ""; - return `
${escapeHtml( - start - )} - ${escapeHtml(end)}${escapeHtml( - caption - )}
`; - }) - .join(""); - pieces.push( - `
${rendered}
` - ); - } - - const meta = []; - if (data && data.language) { - meta.push(`语言: ${escapeHtml(String(data.language))}`); - } - if ( - data && - typeof data.duration === "number" && - isFinite(data.duration) && - data.duration > 0 - ) { - meta.push( - `音频: ${escapeHtml(formatSeconds(data.duration))}` - ); - } - if (meta.length) { - pieces.push( - `
${meta.join(" · ")}
` - ); - } - - return pieces.join(""); - } - - function formatSeconds(value) { - const num = Number(value); - if (!isFinite(num) || num <= 0) { - return "0s"; - } - if (num >= 60) { - const m = Math.floor(num / 60); - const s = Math.round(num % 60); - return `${m}m${s < 10 ? "0" : ""}${s}s`; - } - if (num < 1) { - return `${num.toFixed(2)}s`; - } - return `${Math.round(num)}s`; - } - - document.addEventListener("click", (event) => { - const btn = event.target.closest(".voice-stt-btn"); - if (!btn) return; - if ( - window.ChatlogSpeech && - typeof window.ChatlogSpeech.requestVoice === "function" - ) { - return; - } - event.preventDefault(); - fallbackVoiceRequest(btn); - }); - const diaryDateInput = document.getElementById("diary-date"); if (diaryDateInput) { const today = new Date(); diff --git a/internal/speech/whispercpp/transcriber.go b/internal/speech/whispercpp/transcriber.go index 0b4cc5d..7a2b069 100644 --- a/internal/speech/whispercpp/transcriber.go +++ b/internal/speech/whispercpp/transcriber.go @@ -1,11 +1,5 @@ -//go:build cgo - package whispercpp -/* -#cgo CFLAGS: -I${SRCDIR}/../../../third_party/whisper/include -#cgo LDFLAGS: -L${SRCDIR}/../../../third_party/whisper/lib -lwhisper -lggml -lstdc++ -lm -*/ import "C" import ( diff --git a/third_party/whisper/lib/libggml.a b/third_party/whisper/lib/libggml.a new file mode 100644 index 0000000000000000000000000000000000000000..3c287238ce84a3d280cdc8ead95c7fb2fab575fa GIT binary patch literal 69486 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

mw@;jh?_|Gu<@GE^IfKiEmkwp;kqxPC^oC-j# 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%0D_{QWwY?5%t`Y`d2A*H3N zrK^1{waxz62rLXyL|}DWTts6_Bff2tItKUpl5$GS!s3(|LsHeX;uA2joOE!K!jZg5 zlIY;%NwD@s{T3F3;%iA3#&l$oJ7>l4(EggZj_SUwkdC{@RF{DbOP0p*fsma&S-rDS=ZZ5VS ztaw{%qG`AO2+aRm0IF4sV*hUeNHme6StKqG*jGgUFIvhu)`gV+jrXW@DV9*$Zj=jEYyk0!yJfDJC5CdoF*nJreB%R?+!iNNC?)P@IYPm8u=60v?kK3i zJH>5nVU;?Pq#F2Uw-V-vM5%7~rZ5Fk@-d7v*yxTuX(}NT?#5IMWZg-qk9F$e^EEcN z`R26y{a(3afjb@)vIyVurSB3~;nS}w$cvRVMRc(Wn(Gf#x3|`n)z`QC@sP754YW43 zuEJWAT!92UI-C4+@P?`RSc5}aTJ}RfM9&P=E^S=t!JaH%9kvE`@LHU&v8ADv%O6XX zj}#q^gCX?zd@GxiQTmp3b@|%xMb_4q+NQ?9Y9GGZs%{<)Y=!0deESZ@3L#|6CZ5z- zhC2`Z^=7OFp;Tyg`oq8ZSbb@%qx%w#Ez2rb!D2jh%WKk+l1{R+!1McplPhj{iYFX9TQtw;n@94yGC}hU$CYb|e z6PxbyV%gAB;H$(vIP6(%!jmTLbxjr3^RaDeI)|d3P>r=dtW)80(gA$UT7UMM6;j|^ zlJ6;6=5O)0qbx+RthJpUlNd-{b@A$|BX!j!QP-f||7?$JeM?b6DRZnK^>7tn>6Mzo z0JWTwNqi;n!!$`o|B0?jZi=vm*(Q{>8_-_kHJ#kY&d#qOv*H~F1G-R8L4B*QxwgwE zc73chR0K!Rc(uP`H8%*FIuDp@5i;e6OroCd*w|96Vz-bD)h$}e4>So`3S8a4aeHJki=ryO?YUBcSjo> zs;{oOEzp4kF*jgHk97B*YKzpwk)n$Py2P6?l$vg%B=Y169jHO3)aDFWslRW@vXGWB&wJp$y8xR%MiC!v&U-u-*quwu7oFa*>D=456vpEbz%Ui4)sGGsEg};dlHM1mzYHO1`Ni+0vOBp&_DQsn84WmCxT`x>rzXX+uUE-?w zrDO^>+}u{%j{e-Dj+FqDhtBqPY@O#jIn^sLQBrT2;Hc!`ISE&&>=nVF}WyzRfQ1JaUVxjp6?b-98$C literal 0 HcmV?d00001