1
mirror of https://github.com/jlelse/GoBlog synced 2024-05-30 11:54:28 +00:00

Update Go, some more improvements and fixes

This commit is contained in:
Jan-Lukas Else 2023-02-02 22:59:12 +01:00
parent fd681ffaf3
commit 8f2b7c7101
18 changed files with 131 additions and 137 deletions

View File

@ -126,41 +126,36 @@ func (a *goBlog) apHandleWebfinger(w http.ResponseWriter, r *http.Request) {
}
apIri := a.apIri(blog)
// Encode
buf := bufferpool.Get()
defer bufferpool.Put(buf)
if err := json.NewEncoder(buf).Encode(map[string]any{
"subject": a.webfingerAccts[apIri],
"aliases": []string{
a.webfingerAccts[apIri],
apIri,
},
"links": []map[string]string{
{
"rel": "self",
"type": contenttype.AS,
"href": apIri,
pr, pw := io.Pipe()
go func() {
_ = pw.CloseWithError(json.NewEncoder(pw).Encode(map[string]any{
"subject": a.webfingerAccts[apIri],
"aliases": []string{a.webfingerAccts[apIri], apIri},
"links": []map[string]string{
{
"rel": "self", "type": contenttype.AS, "href": apIri,
},
{
"rel": "http://webfinger.net/rel/profile-page",
"type": "text/html", "href": apIri,
},
},
{
"rel": "http://webfinger.net/rel/profile-page",
"type": "text/html",
"href": apIri,
},
},
}); err != nil {
a.serveError(w, r, "Encoding failed", http.StatusInternalServerError)
return
}
}))
}()
w.Header().Set(contentType, "application/jrd+json"+contenttype.CharsetUtf8Suffix)
_ = a.min.Get().Minify(contenttype.JSON, w, buf)
_ = pr.CloseWithError(a.min.Get().Minify(contenttype.JSON, w, pr))
}
const activityPubMentionsParameter = "activitypubmentions"
func (a *goBlog) apCheckMentions(p *post) {
contentBuf := bufferpool.Get()
a.postHtmlToWriter(contentBuf, &postHtmlOptions{p: p})
links, err := allLinksFromHTML(contentBuf, a.fullPostURL(p))
bufferpool.Put(contentBuf)
pr, pw := io.Pipe()
go func() {
a.postHtmlToWriter(pw, &postHtmlOptions{p: p})
_ = pw.Close()
}()
links, err := allLinksFromHTML(pr, a.fullPostURL(p))
_ = pr.CloseWithError(err)
if err != nil {
log.Println("Failed to extract links from post: " + err.Error())
return

View File

@ -171,6 +171,7 @@ func (a *goBlog) serveAPItem(w http.ResponseWriter, r *http.Request, status int,
return
}
// Send response
w.WriteHeader(status)
w.Header().Set(contentType, contenttype.ASUTF8)
_ = a.min.Get().Minify(contenttype.AS, w, bytes.NewReader(binary))
}

View File

@ -125,6 +125,7 @@ func (a *goBlog) allLinksToCheck(posts ...*post) ([]*stringPair, error) {
_ = pw.Close()
}()
links, err := allLinksFromHTML(pr, a.fullPostURL(post))
_ = pr.CloseWithError(err)
if err != nil {
return nil, err
}

View File

@ -30,7 +30,7 @@ Requirements:
- Linux
- git
- go >= 1.19
- go >= 1.20
- libsqlite3 with FTS5 enabled >= 3.31 (the newer the better)
Build command:

View File

@ -15,11 +15,12 @@ func (a *goBlog) proxyTiles() http.HandlerFunc {
}
return func(w http.ResponseWriter, r *http.Request) {
// Create a new request to proxy to the tile server
targetUrl := tileSource
targetUrl = strings.ReplaceAll(targetUrl, "{s}", chi.URLParam(r, "s"))
targetUrl = strings.ReplaceAll(targetUrl, "{z}", chi.URLParam(r, "z"))
targetUrl = strings.ReplaceAll(targetUrl, "{x}", chi.URLParam(r, "x"))
targetUrl = strings.ReplaceAll(targetUrl, "{y}", chi.URLParam(r, "y"))
targetUrl := strings.NewReplacer(
"{s}", chi.URLParam(r, "s"),
"{z}", chi.URLParam(r, "z"),
"{x}", chi.URLParam(r, "x"),
"{y}", chi.URLParam(r, "y"),
).Replace(tileSource)
proxyRequest, _ := http.NewRequestWithContext(r.Context(), http.MethodGet, targetUrl, nil)
// Copy request headers
for _, k := range []string{
@ -29,7 +30,6 @@ func (a *goBlog) proxyTiles() http.HandlerFunc {
cacheControl,
"If-Modified-Since",
"If-None-Match",
"User-Agent",
} {
proxyRequest.Header.Set(k, r.Header.Get(k))
}

9
go.mod
View File

@ -1,6 +1,6 @@
module go.goblog.app/app
go 1.19
go 1.20
require (
git.jlel.se/jlelse/go-geouri v0.0.0-20210525190615-a9c1d50f42d6
@ -11,7 +11,7 @@ require (
github.com/alecthomas/chroma/v2 v2.4.0
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b
github.com/carlmjohnson/requests v0.23.1
github.com/carlmjohnson/requests v0.23.2
// master
github.com/cretz/bine v0.2.1-0.20221201125941-b9d31d9c7866
github.com/dchest/captcha v1.0.0
@ -61,7 +61,7 @@ require (
github.com/traefik/yaegi v0.14.4-0.20230117132604-1679870ea3c8
github.com/vcraescu/go-paginator v1.0.1-0.20201114172518-2cfc59fe05c2
github.com/xhit/go-simple-mail/v2 v2.13.0
github.com/yuin/goldmark v1.5.3
github.com/yuin/goldmark v1.5.4
// master
github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38
golang.org/x/crypto v0.5.0
@ -71,8 +71,7 @@ require (
gopkg.in/yaml.v3 v3.0.1
maunium.net/go/mautrix v0.13.0
nhooyr.io/websocket v1.8.7
// main
willnorris.com/go/microformats v1.1.2-0.20221115043057-ffbbdaef989e
willnorris.com/go/microformats v1.2.0
)
require (

12
go.sum
View File

@ -77,8 +77,8 @@ github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b h1:6+ZFm0flnudZzdSE0JxlhR2hKnGPcNB35BjQf4RYQDY=
github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
github.com/carlmjohnson/requests v0.23.1 h1:d8FpOZC6We3raxa3cW1oEyK+cNz4XSW8A6wjMkMsbOM=
github.com/carlmjohnson/requests v0.23.1/go.mod h1:i/0+cvgndkM7SPtw911bcbYR4CP+/9vP/+TbcFqPz3A=
github.com/carlmjohnson/requests v0.23.2 h1:SzaY+/5v8QOvt++7HTXe1xgmIb3wc/bYf2QJmrO73sM=
github.com/carlmjohnson/requests v0.23.2/go.mod h1:09VwhOaRQYCraJcByjEuvuOGO1jxUjIx6vnAEkt2ges=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@ -589,8 +589,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.5.3 h1:3HUJmBFbQW9fhQOzMgseU134xfi6hU+mjWywx5Ty+/M=
github.com/yuin/goldmark v1.5.3/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU=
github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38 h1:XZjLcLoTPNZuxppY3gwhRqo/T2XF6JMGFFdkAjX3w1w=
github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@ -993,7 +993,7 @@ nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
willnorris.com/go/microformats v1.1.2-0.20221115043057-ffbbdaef989e h1:9dbX305ANi38nJlQP9ve2v6U1NQp9YFGP3JgrT7Rp7g=
willnorris.com/go/microformats v1.1.2-0.20221115043057-ffbbdaef989e/go.mod h1:RrlwCSvib4qz+JICKiN7rON4phzQ3HAT7j6s4O2cZj4=
willnorris.com/go/microformats v1.2.0 h1:73pzJCLJM69kYE5qsLI9OOC/7sImNVOzya9EQ0+1wmM=
willnorris.com/go/microformats v1.2.0/go.mod h1:RrlwCSvib4qz+JICKiN7rON4phzQ3HAT7j6s4O2cZj4=
willnorris.com/go/webmention v0.0.0-20220108183051-4a23794272f0 h1:V5+O+YZHchEwu6ZmPcqT1dQ+mHgE356Q+w9SVOQ+QZg=
willnorris.com/go/webmention v0.0.0-20220108183051-4a23794272f0/go.mod h1:DgeruqKIsZtcDXVXNbBHa0YYEm88oAnK7PahkDtuCvw=

View File

@ -20,7 +20,7 @@ func (a *goBlog) healthcheck() bool {
fmt.Println("healthcheck:", err.Error())
return false
}
defer resp.Body.Close()
_ = resp.Body.Close()
return resp.StatusCode == 200
}

View File

@ -33,7 +33,7 @@ var (
// Server Metadata
// https://indieauth.spec.indieweb.org/#x4-1-1-indieauth-server-metadata
func (a *goBlog) indieAuthMetadata(w http.ResponseWriter, r *http.Request) {
func (a *goBlog) indieAuthMetadata(w http.ResponseWriter, _ *http.Request) {
resp := map[string]any{
"issuer": a.getInstanceRootURL(),
"authorization_endpoint": a.getFullAddress(indieAuthPath),

View File

@ -87,28 +87,27 @@ func (tf *tinify) compress(url string, upload mediaStorageSaveFunc, hc *http.Cli
return "", tinifyErr
}
// Resize and download image
imgBuffer := bufferpool.Get()
defer bufferpool.Put(imgBuffer)
err = requests.
URL(compressedLocation).
Client(hc).
Method(http.MethodPost).
BasicAuth("api", tf.key).
BodyJSON(map[string]any{
"resize": map[string]any{
"method": "fit",
"width": defaultCompressionWidth,
"height": defaultCompressionHeight,
},
}).
ToBytesBuffer(imgBuffer).
Fetch(context.Background())
if err != nil {
log.Println("Tinify error:", err.Error())
return "", tinifyErr
}
pr, pw := io.Pipe()
go func() {
_ = pw.CloseWithError(requests.
URL(compressedLocation).
Client(hc).
Method(http.MethodPost).
BasicAuth("api", tf.key).
BodyJSON(map[string]any{
"resize": map[string]any{
"method": "fit",
"width": defaultCompressionWidth,
"height": defaultCompressionHeight,
},
}).
ToWriter(pw).
Fetch(context.Background()))
}()
// Upload compressed file
return uploadCompressedFile(fileExtension, imgBuffer, upload)
res, err := uploadCompressedFile(fileExtension, pr, upload)
_ = pr.CloseWithError(err)
return res, err
}
type cloudflare struct{}
@ -121,19 +120,18 @@ func (*cloudflare) compress(url string, upload mediaStorageSaveFunc, hc *http.Cl
// Force jpeg
fileExtension := "jpeg"
// Compress
imgBuffer := bufferpool.Get()
defer bufferpool.Put(imgBuffer)
err := requests.
URL(fmt.Sprintf("https://www.cloudflare.com/cdn-cgi/image/f=jpeg,q=75,metadata=none,fit=scale-down,w=%d,h=%d/%s", defaultCompressionWidth, defaultCompressionHeight, url)).
Client(hc).
ToBytesBuffer(imgBuffer).
Fetch(context.Background())
if err != nil {
log.Println("Cloudflare error:", err.Error())
return "", errors.New("failed to compress image using cloudflare")
}
pr, pw := io.Pipe()
go func() {
_ = pw.CloseWithError(requests.
URL(fmt.Sprintf("https://www.cloudflare.com/cdn-cgi/image/f=jpeg,q=75,metadata=none,fit=scale-down,w=%d,h=%d/%s", defaultCompressionWidth, defaultCompressionHeight, url)).
Client(hc).
ToWriter(pw).
Fetch(context.Background()))
}()
// Upload compressed file
return uploadCompressedFile(fileExtension, imgBuffer, upload)
res, err := uploadCompressedFile(fileExtension, pr, upload)
_ = pr.CloseWithError(err)
return res, err
}
type localMediaCompressor struct{}
@ -144,20 +142,13 @@ func (*localMediaCompressor) compress(url string, upload mediaStorageSaveFunc, h
if !allowed {
return "", nil
}
// Download image
imgBuffer := bufferpool.Get()
defer bufferpool.Put(imgBuffer)
err := requests.
URL(url).
Client(hc).
ToBytesBuffer(imgBuffer).
Fetch(context.Background())
if err != nil {
log.Println("Local compressor error:", err.Error())
return "", errors.New("failed to download image using local compressor")
}
// Decode image
img, err := imaging.Decode(imgBuffer, imaging.AutoOrientation(true))
// Download and decode image
pr, pw := io.Pipe()
go func() {
_ = pw.CloseWithError(requests.URL(url).Client(hc).ToWriter(pw).Fetch(context.Background()))
}()
img, err := imaging.Decode(pr, imaging.AutoOrientation(true))
_ = pr.CloseWithError(err)
if err != nil {
log.Println("Local compressor error:", err.Error())
return "", errors.New("failed to compress image using local compressor")
@ -165,20 +156,19 @@ func (*localMediaCompressor) compress(url string, upload mediaStorageSaveFunc, h
// Resize image
resizedImage := imaging.Fit(img, defaultCompressionWidth, defaultCompressionHeight, imaging.Lanczos)
// Encode image
resizedBuffer := bufferpool.Get()
defer bufferpool.Put(resizedBuffer)
switch fileExtension {
case "jpg", "jpeg":
err = imaging.Encode(resizedBuffer, resizedImage, imaging.JPEG, imaging.JPEGQuality(75))
case "png":
err = imaging.Encode(resizedBuffer, resizedImage, imaging.PNG, imaging.PNGCompressionLevel(png.BestCompression))
}
if err != nil {
log.Println("Local compressor error:", err.Error())
return "", errors.New("failed to compress image using local compressor")
}
pr, pw = io.Pipe()
go func() {
switch fileExtension {
case "png":
_ = pw.CloseWithError(imaging.Encode(pw, resizedImage, imaging.PNG, imaging.PNGCompressionLevel(png.BestCompression)))
default:
_ = pw.CloseWithError(imaging.Encode(pw, resizedImage, imaging.JPEG, imaging.JPEGQuality(75)))
}
}()
// Upload compressed file
return uploadCompressedFile(fileExtension, resizedBuffer, upload)
res, err := uploadCompressedFile(fileExtension, pr, upload)
_ = pr.CloseWithError(err)
return res, err
}
func uploadCompressedFile(fileExtension string, r io.Reader, upload mediaStorageSaveFunc) (string, error) {
@ -186,7 +176,10 @@ func uploadCompressedFile(fileExtension string, r io.Reader, upload mediaStorage
hash := sha256.New()
tempBuffer := bufferpool.Get()
defer bufferpool.Put(tempBuffer)
_, _ = io.Copy(io.MultiWriter(tempBuffer, hash), r)
_, err := io.Copy(io.MultiWriter(tempBuffer, hash), r)
if err != nil {
return "", err
}
// Upload buffer
return upload(fmt.Sprintf("%x.%s", hash.Sum(nil), fileExtension), tempBuffer)
}

View File

@ -29,12 +29,14 @@ func (a *goBlog) mediaStorageEnabled() bool {
return a.mediaStorage != nil
}
var errNoMediaStorageConfigured = errors.New("no media storage configured")
type mediaStorageSaveFunc func(filename string, file io.Reader) (location string, err error)
func (a *goBlog) saveMediaFile(filename string, f io.Reader) (string, error) {
a.initMediaStorage()
if a.mediaStorage == nil {
return "", errors.New("no media storage configured")
return "", errNoMediaStorageConfigured
}
loc, err := a.mediaStorage.save(filename, f)
if err != nil {
@ -46,7 +48,7 @@ func (a *goBlog) saveMediaFile(filename string, f io.Reader) (string, error) {
func (a *goBlog) deleteMediaFile(filename string) error {
a.initMediaStorage()
if a.mediaStorage == nil {
return errors.New("no media storage configured")
return errNoMediaStorageConfigured
}
return a.mediaStorage.delete(filepath.Base(filename))
}
@ -61,7 +63,7 @@ type mediaFile struct {
func (a *goBlog) mediaFiles() ([]*mediaFile, error) {
a.initMediaStorage()
if a.mediaStorage == nil {
return nil, errors.New("no media storage configured")
return nil, errNoMediaStorageConfigured
}
return a.mediaStorage.files()
}

View File

@ -8,7 +8,7 @@ import (
"go.goblog.app/app/pkgs/contenttype"
)
func (a *goBlog) serveNodeInfoDiscover(w http.ResponseWriter, r *http.Request) {
func (a *goBlog) serveNodeInfoDiscover(w http.ResponseWriter, _ *http.Request) {
result := map[string]any{
"links": []map[string]any{
{
@ -25,7 +25,7 @@ func (a *goBlog) serveNodeInfoDiscover(w http.ResponseWriter, r *http.Request) {
_ = pr.CloseWithError(a.min.Get().Minify(contenttype.JSON, w, pr))
}
func (a *goBlog) serveNodeInfo(w http.ResponseWriter, r *http.Request) {
func (a *goBlog) serveNodeInfo(w http.ResponseWriter, _ *http.Request) {
localPosts, _ := a.db.countPosts(&postsRequestConfig{
status: []postStatus{statusPublished},
visibility: []postVisibility{visibilityPublic},

View File

@ -19,7 +19,6 @@ import (
_ "embed"
"github.com/disintegration/imaging"
"go.goblog.app/app/pkgs/bufferpool"
"go.goblog.app/app/pkgs/contenttype"
)
@ -91,19 +90,20 @@ func (a *goBlog) serveProfileImage(format profileImageFormat) http.HandlerFunc {
quality = 75
}
// Read from database
var imageBytes []byte
var imageReader io.ReadCloser
if a.hasProfileImage() {
var err error
imageBytes, err = os.ReadFile(profileImageFile)
if err != nil || imageBytes == nil {
a.serveError(w, r, "Failed to read image file", http.StatusInternalServerError)
imageReader, err = os.Open(profileImageFile)
if err != nil {
a.serveError(w, r, "Failed to open image file", http.StatusInternalServerError)
return
}
} else {
imageBytes = defaultLogo
imageReader = io.NopCloser(bytes.NewReader(defaultLogo))
}
// Decode image
img, err := imaging.Decode(bytes.NewReader(imageBytes), imaging.AutoOrientation(true))
img, err := imaging.Decode(imageReader, imaging.AutoOrientation(true))
_ = imageReader.Close()
if err != nil {
a.serveError(w, r, "Failed to decode image", http.StatusInternalServerError)
return
@ -111,16 +111,14 @@ func (a *goBlog) serveProfileImage(format profileImageFormat) http.HandlerFunc {
// Resize image
resizedImage := imaging.Fit(img, width, height, imaging.Lanczos)
// Encode
resizedBuffer := bufferpool.Get()
defer bufferpool.Put(resizedBuffer)
err = encode(resizedBuffer, resizedImage, quality)
if err != nil {
a.serveError(w, r, "Failed to encode image", http.StatusInternalServerError)
return
}
pr, pw := io.Pipe()
go func() {
_ = pw.CloseWithError(encode(pw, resizedImage, quality))
}()
// Return
w.Header().Set(contentType, mediaType)
_, _ = io.Copy(w, resizedBuffer)
_, err = io.Copy(w, pr)
_ = pr.CloseWithError(err)
}
}

View File

@ -43,7 +43,7 @@ func (a *goBlog) renderWithStatusCode(w http.ResponseWriter, r *http.Request, st
renderPipeReader, renderPipeWriter := io.Pipe()
go func() {
f(htmlbuilder.NewHtmlBuilder(renderPipeWriter), data)
renderPipeWriter.Close()
_ = renderPipeWriter.Close()
}()
// Run UI plugins
pluginPipeReader, pluginPipeWriter := io.Pipe()
@ -52,7 +52,7 @@ func (a *goBlog) renderWithStatusCode(w http.ResponseWriter, r *http.Request, st
blog: data.BlogString,
path: r.URL.Path,
}, renderPipeReader, pluginPipeWriter)
pluginPipeWriter.Close()
_ = pluginPipeWriter.Close()
}()
// Return minified HTML
_ = pluginPipeReader.CloseWithError(a.min.Get().Minify(contenttype.HTML, w, pluginPipeReader))
@ -69,6 +69,7 @@ func (a *goBlog) chainUiPlugins(plugins []any, rc *pluginRenderContext, rendered
_ = writer.Close()
}()
a.chainUiPlugins(plugins[1:], rc, reader, modified)
_ = reader.Close()
}
func (a *goBlog) checkRenderData(r *http.Request, data *renderData) {

View File

@ -173,7 +173,7 @@ func (a *goBlog) serveSitemapBlogPosts(w http.ResponseWriter, r *http.Request) {
a.writeSitemapXML(w, r, sm)
}
func (a *goBlog) writeSitemapXML(w http.ResponseWriter, r *http.Request, sm any) {
func (a *goBlog) writeSitemapXML(w http.ResponseWriter, _ *http.Request, sm any) {
pr, pw := io.Pipe()
go func() {
_, _ = io.WriteString(pw, xml.Header)

9
tts.go
View File

@ -75,6 +75,7 @@ func (a *goBlog) createPostTTSAudio(p *post) error {
_ = phw.Close()
}()
postHtmlText, err := htmlTextFromReader(phr)
_ = phr.CloseWithError(err)
if err != nil {
return err
}
@ -82,14 +83,13 @@ func (a *goBlog) createPostTTSAudio(p *post) error {
// Create TTS audio for each part
partReaders := []io.Reader{}
partWriters := []*io.PipeWriter{}
var g errgroup.Group
for _, part := range parts {
part := part
pr, pw := io.Pipe()
defer func() {
pw.Close()
}()
partReaders = append(partReaders, pr)
partWriters = append(partWriters, pw)
g.Go(func() error {
// Build SSML
ssml := "<speak>" + html.EscapeString(part) + "<break time=\"500ms\"/></speak>"
@ -105,6 +105,9 @@ func (a *goBlog) createPostTTSAudio(p *post) error {
defer bufferpool.Put(buf)
hash := sha256.New()
err = mp3merge.MergeMP3(io.MultiWriter(buf, hash), partReaders...)
for _, pw := range partWriters {
_ = pw.CloseWithError(err)
}
if err != nil {
return err
}

View File

@ -6,7 +6,6 @@ EXTRA="
github.com/cretz/bine@master
github.com/tkrajina/gpxgo@master
github.com/yuin/goldmark-emoji@master
willnorris.com/go/microformats@main
github.com/traefik/yaegi@master
"

View File

@ -289,7 +289,9 @@ func cleanHTMLText(s string) string {
// Clean HTML with UGC policy and return text
pr, pw := io.Pipe()
go func() { _ = pw.CloseWithError(bluemonday.UGCPolicy().SanitizeReaderToWriter(strings.NewReader(s), pw)) }()
s, _ = htmlTextFromReader(pr)
var err error
s, err = htmlTextFromReader(pr)
_ = pr.CloseWithError(err)
return s
}