From 68daab64e3cf7a45b8593bc4749baec1ab6a116b Mon Sep 17 00:00:00 2001 From: Jan-Lukas Else Date: Fri, 11 Feb 2022 16:19:10 +0100 Subject: [PATCH] Use more writers and streaming when possible --- activityStreams.go | 2 +- check.go | 6 ++- editor.go | 10 ++-- feeds.go | 7 ++- markdown.go | 14 ------ markdown_test.go | 5 -- micropub.go | 3 +- posts.go | 4 -- postsFuncs.go | 106 +++++++++++++++++++------------------------ tts.go | 2 +- ui.go | 68 ++++++++++----------------- uiComponents.go | 27 ++++++----- uiHtmlBuilder.go | 5 -- utils.go | 36 +++++++++------ webmentionSending.go | 6 ++- 15 files changed, 130 insertions(+), 171 deletions(-) diff --git a/activityStreams.go b/activityStreams.go index 6852171..747f227 100644 --- a/activityStreams.go +++ b/activityStreams.go @@ -113,7 +113,7 @@ func (a *goBlog) toASNote(p *post) *asNote { as.Type = "Note" } // Content - as.Content = string(a.postHtml(p, true)) + as.Content = a.postHtml(p, true) // Attachments if images := p.Parameters[a.cfg.Micropub.PhotoParam]; len(images) > 0 { for _, image := range images { diff --git a/check.go b/check.go index c441b2b..a11ac16 100644 --- a/check.go +++ b/check.go @@ -12,6 +12,7 @@ import ( "sync/atomic" "time" + "go.goblog.app/app/pkgs/bufferpool" "golang.org/x/sync/singleflight" ) @@ -126,7 +127,10 @@ func (a *goBlog) checkLinks(w io.Writer, posts ...*post) error { func (a *goBlog) allLinks(posts ...*post) (allLinks []*stringPair, err error) { for _, p := range posts { - links, err := allLinksFromHTMLString(string(a.postHtml(p, true)), a.fullPostURL(p)) + contentBuf := bufferpool.Get() + a.postHtmlToWriter(contentBuf, p, true) + links, err := allLinksFromHTML(contentBuf, a.fullPostURL(p)) + bufferpool.Put(contentBuf) if err != nil { return nil, err } diff --git a/editor.go b/editor.go index 6af594a..36e5f40 100644 --- a/editor.go +++ b/editor.go @@ -45,7 +45,7 @@ func (a *goBlog) serveEditorPreview(w http.ResponseWriter, r *http.Request) { continue } // Create preview - preview, err := a.createMarkdownPreview(blog, message) + preview, err := a.createMarkdownPreview(blog, string(message)) if err != nil { preview = []byte(err.Error()) } @@ -57,12 +57,10 @@ func (a *goBlog) serveEditorPreview(w http.ResponseWriter, r *http.Request) { } } -func (a *goBlog) createMarkdownPreview(blog string, markdown []byte) (rendered []byte, err error) { +func (a *goBlog) createMarkdownPreview(blog string, markdown string) (rendered []byte, err error) { p := &post{ - Content: string(markdown), - Blog: blog, - Path: "/editor/preview", - Published: localNowString(), + Blog: blog, + Content: markdown, } err = a.computeExtraPostParameters(p) if err != nil { diff --git a/feeds.go b/feeds.go index 79012f7..c9e7466 100644 --- a/feeds.go +++ b/feeds.go @@ -8,6 +8,7 @@ import ( "github.com/araddon/dateparse" "github.com/jlelse/feeds" + "go.goblog.app/app/pkgs/bufferpool" "go.goblog.app/app/pkgs/contenttype" ) @@ -38,16 +39,18 @@ func (a *goBlog) generateFeed(blog string, f feedType, w http.ResponseWriter, r }, } for _, p := range posts { - content, _ := a.min.MinifyString(contenttype.HTML, a.feedHtml(p)) + buf := bufferpool.Get() + a.feedHtml(buf, p) feed.Add(&feeds.Item{ Title: p.RenderedTitle, Link: &feeds.Link{Href: a.fullPostURL(p)}, Description: a.postSummary(p), Id: p.Path, - Content: content, + Content: buf.String(), Created: timeNoErr(dateparse.ParseLocal(p.Published)), Updated: timeNoErr(dateparse.ParseLocal(p.Updated)), }) + bufferpool.Put(buf) } var err error var feedBuffer bytes.Buffer diff --git a/markdown.go b/markdown.go index 77ce46b..53255fa 100644 --- a/markdown.go +++ b/markdown.go @@ -2,7 +2,6 @@ package main import ( "bytes" - "html/template" "io" marktag "git.jlel.se/jlelse/goldmark-mark" @@ -94,19 +93,6 @@ func (a *goBlog) renderMarkdownToWriter(w io.Writer, source string, absoluteLink return err } -func (a *goBlog) renderMarkdownAsHTML(source string, absoluteLinks bool) (rendered template.HTML, err error) { - b, err := a.renderMarkdown(source, absoluteLinks) - if err != nil { - return "", err - } - return template.HTML(b), nil -} - -func (a *goBlog) safeRenderMarkdownAsHTML(source string) template.HTML { - h, _ := a.renderMarkdownAsHTML(source, false) - return h -} - func (a *goBlog) renderText(s string) string { if s == "" { return "" diff --git a/markdown_test.go b/markdown_test.go index 090c889..5289f53 100644 --- a/markdown_test.go +++ b/markdown_test.go @@ -79,11 +79,6 @@ func Test_markdown(t *testing.T) { assert.Equal(t, "Test’s", app.renderMdTitle("Test's")) assert.Equal(t, "😂", app.renderMdTitle(":joy:")) assert.Equal(t, "", app.renderMdTitle("")) - - // Template func - - renderedText = string(app.safeRenderMarkdownAsHTML("[Relative](/relative)")) - assert.Contains(t, renderedText, `href="/relative"`) }) } diff --git a/micropub.go b/micropub.go index d8c3418..c7838a6 100644 --- a/micropub.go +++ b/micropub.go @@ -10,6 +10,7 @@ import ( "regexp" "strconv" "strings" + "time" "github.com/spf13/cast" "github.com/thoas/go-funk" @@ -397,7 +398,7 @@ func (a *goBlog) computeExtraPostParameters(p *post) error { } if p.Published == "" && p.Section != "" { // Has no published date, but section -> published now - p.Published = localNowString() + p.Published = time.Now().Local().Format(time.RFC3339) } // Add images not in content images := p.Parameters[a.cfg.Micropub.PhotoParam] diff --git a/posts.go b/posts.go index ccf92bd..4af2723 100644 --- a/posts.go +++ b/posts.go @@ -4,12 +4,10 @@ import ( "context" "errors" "fmt" - "html/template" "net/http" "reflect" "strconv" "strings" - "sync" "time" "github.com/go-chi/chi/v5" @@ -31,8 +29,6 @@ type post struct { // Not persisted Slug string RenderedTitle string - renderCache map[bool]template.HTML - renderMutex sync.RWMutex } type postStatus string diff --git a/postsFuncs.go b/postsFuncs.go index deb2c77..80d073d 100644 --- a/postsFuncs.go +++ b/postsFuncs.go @@ -2,13 +2,13 @@ package main import ( "fmt" - "html/template" - "log" + "io" "strings" "time" gogeouri "git.jlel.se/jlelse/go-geouri" "github.com/araddon/dateparse" + "go.goblog.app/app/pkgs/bufferpool" "gopkg.in/yaml.v3" ) @@ -34,69 +34,56 @@ func (p *post) firstParameter(parameter string) (result string) { return } -func (a *goBlog) postHtml(p *post, absolute bool) template.HTML { - p.renderMutex.RLock() - // Check cache - if r, ok := p.renderCache[absolute]; ok && r != "" { - p.renderMutex.RUnlock() - return r - } - p.renderMutex.RUnlock() - // No cache, build it... - p.renderMutex.Lock() - defer p.renderMutex.Unlock() - // Build HTML - var htmlBuilder strings.Builder - // Add audio to the top - for _, a := range p.Parameters[a.cfg.Micropub.AudioParam] { - htmlBuilder.WriteString(``) - } - // Render markdown - htmlContent, err := a.renderMarkdown(p.Content, absolute) - if err != nil { - log.Fatal(err) - return "" - } - htmlBuilder.Write(htmlContent) - // Add bookmark links to the bottom - for _, l := range p.Parameters[a.cfg.Micropub.BookmarkParam] { - htmlBuilder.WriteString(`

`) - htmlBuilder.WriteString(l) - htmlBuilder.WriteString(`

`) - } - // Cache - html := template.HTML(htmlBuilder.String()) - if p.renderCache == nil { - p.renderCache = map[bool]template.HTML{} - } - p.renderCache[absolute] = html - return html +func (a *goBlog) postHtml(p *post, absolute bool) (res string) { + buf := bufferpool.Get() + a.postHtmlToWriter(buf, p, absolute) + res = buf.String() + bufferpool.Put(buf) + return } -func (a *goBlog) feedHtml(p *post) string { - var htmlBuilder strings.Builder +func (a *goBlog) postHtmlToWriter(w io.Writer, p *post, absolute bool) { + // Build HTML + hb := newHtmlBuilder(w) + // Add audio to the top + for _, a := range p.Parameters[a.cfg.Micropub.AudioParam] { + hb.writeElementOpen("audio", "controls", "preload", "none") + hb.writeElementOpen("source", "src", a) + hb.writeElementClose("source") + hb.writeElementClose("audio") + } + // Render markdown + _ = a.renderMarkdownToWriter(w, p.Content, absolute) + // Add bookmark links to the bottom + for _, l := range p.Parameters[a.cfg.Micropub.BookmarkParam] { + hb.writeElementOpen("p") + hb.writeElementOpen("a", "class", "u-bookmark-of", "href", l, "target", "_blank", "rel", "noopener noreferrer") + hb.writeEscaped(l) + hb.writeElementClose("a") + hb.writeElementClose("p") + } +} + +func (a *goBlog) feedHtml(w io.Writer, p *post) { + hb := newHtmlBuilder(w) // Add TTS audio to the top for _, a := range p.Parameters[ttsParameter] { - htmlBuilder.WriteString(``) + hb.writeElementOpen("audio", "controls", "preload", "none") + hb.writeElementOpen("source", "src", a) + hb.writeElementClose("source") + hb.writeElementClose("audio") } // Add post HTML - htmlBuilder.WriteString(string(a.postHtml(p, true))) + a.postHtmlToWriter(hb, p, true) // Add link to interactions and comments blogConfig := a.cfg.Blogs[defaultIfEmpty(p.Blog, a.cfg.DefaultBlog)] if cc := blogConfig.Comments; cc != nil && cc.Enabled { - htmlBuilder.WriteString(`

`) - htmlBuilder.WriteString(a.ts.GetTemplateStringVariant(blogConfig.Lang, "interactions")) - htmlBuilder.WriteString(`

`) + hb.writeElementOpen("p") + hb.writeElementOpen("a", "href", a.getFullAddress(p.Path)+"#interactions") + hb.writeEscaped(a.ts.GetTemplateStringVariant(blogConfig.Lang, "interactions")) + hb.writeElementClose("a") + hb.writeElementClose("p") } - return htmlBuilder.String() } const summaryDivider = "" @@ -106,11 +93,12 @@ func (a *goBlog) postSummary(p *post) (summary string) { if summary != "" { return } - html := string(a.postHtml(p, false)) - if splitted := strings.Split(html, summaryDivider); len(splitted) > 1 { - summary = htmlText(splitted[0]) + if splitted := strings.Split(p.Content, summaryDivider); len(splitted) > 1 { + rendered, _ := a.renderMarkdown(splitted[0], false) + summary = htmlText(string(rendered)) } else { - summary = strings.Split(htmlText(html), "\n\n")[0] + rendered, _ := a.renderMarkdown(splitted[0], false) + summary = strings.Split(htmlText(string(rendered)), "\n\n")[0] } return } diff --git a/tts.go b/tts.go index 23889bb..638a7af 100644 --- a/tts.go +++ b/tts.go @@ -65,7 +65,7 @@ func (a *goBlog) createPostTTSAudio(p *post) error { parts = append(parts, a.renderMdTitle(title)) } // Add body split into paragraphs because of 5000 character limit - parts = append(parts, strings.Split(htmlText(string(a.postHtml(p, false))), "\n\n")...) + parts = append(parts, strings.Split(htmlText(a.postHtml(p, false)), "\n\n")...) // Create TTS audio for each part partsBuffers := make([]io.Reader, len(parts)) diff --git a/ui.go b/ui.go index a624e8c..3996f0f 100644 --- a/ui.go +++ b/ui.go @@ -2,7 +2,6 @@ package main import ( "fmt" - "strings" "time" "github.com/hacdias/indieauth" @@ -20,7 +19,7 @@ func (a *goBlog) renderEditorPreview(hb *htmlBuilder, bc *configBlog, p *post) { a.renderPostMeta(hb, p, bc, "preview") if p.Content != "" { hb.writeElementOpen("div") - hb.writeHtml(a.postHtml(p, true)) + a.postHtmlToWriter(hb, p, true) hb.writeElementClose("div") } a.renderPostTax(hb, p, bc) @@ -859,14 +858,11 @@ func (a *goBlog) renderPost(hb *htmlBuilder, rd *renderData) { if !ok { return } - postHtml := a.postHtml(p, false) a.renderBase( hb, rd, func(hb *htmlBuilder) { a.renderTitleTag(hb, rd.Blog, p.RenderedTitle) - if strings.Contains(string(postHtml), "c-chroma") { - hb.writeElementOpen("link", "rel", "stylesheet", "href", a.assetFileName("css/chroma.css")) - } + hb.writeElementOpen("link", "rel", "stylesheet", "href", a.assetFileName("css/chroma.css")) a.renderPostHeadMeta(hb, p, rd.Canonical) if su := a.shortPostURL(p); su != "" { hb.writeElementOpen("link", "rel", "shortlink", "href", su) @@ -924,7 +920,7 @@ func (a *goBlog) renderPost(hb *htmlBuilder, rd *renderData) { if p.Content != "" { // Content hb.writeElementOpen("div", "class", "e-content") - hb.writeHtml(postHtml) + a.postHtmlToWriter(hb, p, false) hb.writeElementClose("div") } // GPS Track @@ -1027,7 +1023,7 @@ func (a *goBlog) renderStaticHome(hb *htmlBuilder, rd *renderData) { if p.Content != "" { // Content hb.writeElementOpen("div", "class", "e-content") - hb.writeHtml(a.postHtml(p, false)) + a.postHtmlToWriter(hb, p, false) hb.writeElementClose("div") } // Author @@ -1142,7 +1138,7 @@ func (a *goBlog) renderEditorFiles(hb *htmlBuilder, rd *renderData) { usesString := a.ts.GetTemplateStringVariant(rd.Blog.Lang, "fileuses") for i, f := range ef.files { hb.writeElementOpen("option", "value", f.Name) - hb.writeEscaped(fmt.Sprintf("%s (%s), %s, ~%d %s", f.Name, isoDateFormat(f.Time.String()), mBytesString(f.Size), ef.uses[i], usesString)) + hb.writeEscaped(fmt.Sprintf("%s (%s), %s, ~%d %s", f.Name, f.Time.Local().Format(isoDateFormat), mBytesString(f.Size), ef.uses[i], usesString)) hb.writeElementClose("option") } hb.writeElementClose("select") @@ -1305,6 +1301,7 @@ func (a *goBlog) renderWebmentionAdmin(hb *htmlBuilder, rd *renderData) { hb.writeEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, "webmentions")) hb.writeElementClose("h1") // Notifications + tdLocale := matchTimeDiffLocale(rd.Blog.Lang) for _, m := range wrd.mentions { hb.writeElementOpen("div", "id", fmt.Sprintf("mention-%d", m.ID), "class", "p") hb.writeElementOpen("p") @@ -1330,7 +1327,7 @@ func (a *goBlog) renderWebmentionAdmin(hb *htmlBuilder, rd *renderData) { hb.writeElementOpen("br") // Date hb.writeEscaped("Created: ") - hb.writeEscaped(unixToLocalDateString(m.Created)) + hb.writeEscaped(timediff.TimeDiff(time.Unix(m.Created, 0), timediff.WithLocale(tdLocale))) hb.writeElementOpen("br") hb.writeElementOpen("br") // Author @@ -1447,36 +1444,24 @@ func (a *goBlog) renderEditor(hb *htmlBuilder, rd *renderData) { hb.writeElementOpen("h2") hb.writeEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, "posts")) hb.writeElementClose("h2") + // Template + postsListLink := func(path, title string) { + hb.writeElementOpen("p") + hb.writeElementOpen("a", "href", rd.Blog.getRelativePath(path)) + hb.writeEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, title)) + hb.writeElementClose("a") + hb.writeElementClose("p") + } // Drafts - hb.writeElementOpen("p") - hb.writeElementOpen("a", "href", rd.Blog.getRelativePath("/editor/drafts")) - hb.writeEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, "drafts")) - hb.writeElementClose("a") - hb.writeElementClose("p") + postsListLink("/editor/drafts", "drafts") // Private - hb.writeElementOpen("p") - hb.writeElementOpen("a", "href", rd.Blog.getRelativePath("/editor/private")) - hb.writeEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, "privateposts")) - hb.writeElementClose("a") - hb.writeElementClose("p") + postsListLink("/editor/private", "privateposts") // Unlisted - hb.writeElementOpen("p") - hb.writeElementOpen("a", "href", rd.Blog.getRelativePath("/editor/unlisted")) - hb.writeEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, "unlistedposts")) - hb.writeElementClose("a") - hb.writeElementClose("p") + postsListLink("/editor/unlisted", "unlistedposts") // Scheduled - hb.writeElementOpen("p") - hb.writeElementOpen("a", "href", rd.Blog.getRelativePath("/editor/scheduled")) - hb.writeEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, "scheduledposts")) - hb.writeElementClose("a") - hb.writeElementClose("p") + postsListLink("/editor/scheduled", "scheduledposts") // Deleted - hb.writeElementOpen("p") - hb.writeElementOpen("a", "href", rd.Blog.getRelativePath("/editor/deleted")) - hb.writeEscaped(a.ts.GetTemplateStringVariant(rd.Blog.Lang, "deletedposts")) - hb.writeElementClose("a") - hb.writeElementClose("p") + postsListLink("/editor/deleted", "deletedposts") // Upload hb.writeElementOpen("h2") @@ -1524,15 +1509,10 @@ func (a *goBlog) renderEditor(hb *htmlBuilder, rd *renderData) { hb.writeElementClose("main") // Scripts - // Editor preview - hb.writeElementOpen("script", "src", a.assetFileName("js/mdpreview.js"), "defer", "") - hb.writeElementClose("script") - // Geohelper - hb.writeElementOpen("script", "src", a.assetFileName("js/geohelper.js"), "defer", "") - hb.writeElementClose("script") - // Formcache - hb.writeElementOpen("script", "src", a.assetFileName("js/formcache.js"), "defer", "") - hb.writeElementClose("script") + for _, script := range []string{"js/mdpreview.js", "js/geohelper.js", "js/formcache.js"} { + hb.writeElementOpen("script", "src", a.assetFileName(script), "defer", "") + hb.writeElementClose("script") + } }, ) } diff --git a/uiComponents.go b/uiComponents.go index 576ed14..5d2e62b 100644 --- a/uiComponents.go +++ b/uiComponents.go @@ -1,6 +1,9 @@ package main -import "fmt" +import ( + "fmt" + "time" +) type summaryTyp string @@ -46,7 +49,7 @@ func (a *goBlog) renderSummary(hb *htmlBuilder, bc *configBlog, p *post, typ sum if typ != photoSummary && a.showFull(p) { // Show full content hb.writeElementOpen("div", "class", "e-content") - hb.write(string(a.postHtml(p, false))) + a.postHtmlToWriter(hb, p, false) hb.writeElementClose("div") } else { // Show summary @@ -114,12 +117,12 @@ func (a *goBlog) renderPostMeta(hb *htmlBuilder, p *post, b *configBlog, typ str hb.writeElementOpen("div", "class", "p") } // Published time - if published := p.Published; published != "" { + if published := toLocalTime(p.Published); !published.IsZero() { hb.writeElementOpen("div") hb.writeEscaped(a.ts.GetTemplateStringVariant(b.Lang, "publishedon")) hb.write(" ") - hb.writeElementOpen("time", "class", "dt-published", "datetime", dateFormat(published, "2006-01-02T15:04:05Z07:00")) - hb.writeEscaped(isoDateFormat(published)) + hb.writeElementOpen("time", "class", "dt-published", "datetime", published.Format(time.RFC3339)) + hb.writeEscaped(published.Format(isoDateFormat)) hb.writeElementClose("time") // Section if p.Section != "" { @@ -133,12 +136,12 @@ func (a *goBlog) renderPostMeta(hb *htmlBuilder, p *post, b *configBlog, typ str hb.writeElementClose("div") } // Updated time - if updated := p.Updated; updated != "" { + if updated := toLocalTime(p.Updated); !updated.IsZero() { hb.writeElementOpen("div") hb.writeEscaped(a.ts.GetTemplateStringVariant(b.Lang, "updatedon")) hb.write(" ") - hb.writeElementOpen("time", "class", "dt-updated", "datetime", dateFormat(updated, "2006-01-02T15:04:05Z07:00")) - hb.writeEscaped(isoDateFormat(updated)) + hb.writeElementOpen("time", "class", "dt-updated", "datetime", updated.Format(time.RFC3339)) + hb.writeEscaped(updated.Format(isoDateFormat)) hb.writeElementClose("time") hb.writeElementClose("div") } @@ -335,11 +338,11 @@ func (a *goBlog) renderPostHeadMeta(hb *htmlBuilder, p *post, canonical string) hb.writeElementOpen("meta", "property", "og:description", "content", summary) hb.writeElementOpen("meta", "property", "twitter:description", "content", summary) } - if p.Published != "" { - hb.writeElementOpen("meta", "itemprop", "datePublished", "content", dateFormat(p.Published, "2006-01-02T15:04:05-07:00")) + if published := toLocalTime(p.Published); !published.IsZero() { + hb.writeElementOpen("meta", "itemprop", "datePublished", "content", published.Format(time.RFC3339)) } - if p.Updated != "" { - hb.writeElementOpen("meta", "itemprop", "dateModified", "content", dateFormat(p.Updated, "2006-01-02T15:04:05-07:00")) + if updated := toLocalTime(p.Updated); !updated.IsZero() { + hb.writeElementOpen("meta", "itemprop", "dateModified", "content", updated.Format(time.RFC3339)) } for _, img := range a.photoLinks(p) { hb.writeElementOpen("meta", "itemprop", "image", "content", img) diff --git a/uiHtmlBuilder.go b/uiHtmlBuilder.go index 61b1948..c64c38a 100644 --- a/uiHtmlBuilder.go +++ b/uiHtmlBuilder.go @@ -2,7 +2,6 @@ package main import ( "fmt" - "html/template" "io" textTemplate "text/template" ) @@ -33,10 +32,6 @@ func (h *htmlBuilder) write(s string) { _, _ = h.WriteString(s) } -func (h *htmlBuilder) writeHtml(s template.HTML) { - h.write(string(s)) -} - func (h *htmlBuilder) writeEscaped(s string) { textTemplate.HTMLEscape(h, []byte(s)) } diff --git a/utils.go b/utils.go index ba957b2..0dad579 100644 --- a/utils.go +++ b/utils.go @@ -14,6 +14,7 @@ import ( "path/filepath" "sort" "strings" + "sync" "time" "unicode" @@ -164,25 +165,18 @@ func toUTC(s string) (string, error) { return d.UTC().Format(time.RFC3339), nil } -func dateFormat(date string, format string) string { +func toLocalTime(date string) time.Time { + if date == "" { + return time.Time{} + } d, err := dateparse.ParseLocal(date) if err != nil { - return "" + return time.Time{} } - return d.Local().Format(format) + return d.Local() } -func isoDateFormat(date string) string { - return dateFormat(date, "2006-01-02") -} - -func unixToLocalDateString(unix int64) string { - return time.Unix(unix, 0).Local().Format(time.RFC3339) -} - -func localNowString() string { - return time.Now().Local().Format(time.RFC3339) -} +const isoDateFormat = "2006-01-02" func utcNowString() string { return time.Now().UTC().Format(time.RFC3339) @@ -366,7 +360,17 @@ func (valueOnlyContext) Err() error { return nil } +var timeDiffLocaleMap = map[string]tdl.Locale{} +var timeDiffLocaleMutex sync.RWMutex + func matchTimeDiffLocale(lang string) tdl.Locale { + timeDiffLocaleMutex.RLock() + if locale, ok := timeDiffLocaleMap[lang]; ok { + return locale + } + timeDiffLocaleMutex.RUnlock() + timeDiffLocaleMutex.Lock() + defer timeDiffLocaleMutex.Unlock() supportedLangs := []string{"en", "de", "es", "hi", "pt", "ru", "zh-CN"} supportedTags := []language.Tag{} for _, lang := range supportedLangs { @@ -374,5 +378,7 @@ func matchTimeDiffLocale(lang string) tdl.Locale { } matcher := language.NewMatcher(supportedTags) _, idx, _ := matcher.Match(language.Make(lang)) - return tdl.Locale(supportedLangs[idx]) + locale := tdl.Locale(supportedLangs[idx]) + timeDiffLocaleMap[lang] = locale + return locale } diff --git a/webmentionSending.go b/webmentionSending.go index ddb4397..6c23d25 100644 --- a/webmentionSending.go +++ b/webmentionSending.go @@ -14,6 +14,7 @@ import ( "github.com/carlmjohnson/requests" "github.com/thoas/go-funk" "github.com/tomnomnom/linkheader" + "go.goblog.app/app/pkgs/bufferpool" ) const postParamWebmention = "webmention" @@ -32,7 +33,10 @@ func (a *goBlog) sendWebmentions(p *post) error { return nil } links := []string{} - contentLinks, err := allLinksFromHTML(strings.NewReader(string(a.postHtml(p, false))), a.fullPostURL(p)) + contentBuf := bufferpool.Get() + a.postHtmlToWriter(contentBuf, p, false) + contentLinks, err := allLinksFromHTML(contentBuf, a.fullPostURL(p)) + bufferpool.Put(contentBuf) if err != nil { return err }