Files
sm-bm-plugin/jobs.go
Jakub Zych 412821ec56 feat: index the text of attached PDFs
- jz_bm_material_file_texts caches the pdftotext output per file
- the River job jz.bm.extract_material_text extracts and resyncs
- the management multipart attach queues it inside its transaction
- file_texts in the document and a file snippet with its name
2026-11-08 20:11:19 +01:00

48 lines
1.5 KiB
Go

package bm
import (
"context"
"errors"
"sync"
"git.golem15.com/golem15/summercms/modules/diary"
"git.golem15.com/jakub/sm-bm-plugin/classes"
"git.golem15.com/jakub/sm-bm-plugin/jobs"
"gocloud.dev/blob"
"gorm.io/gorm"
)
// pdfToolWarning is logged at most once per process when pdftotext is
// missing.
var pdfToolWarning sync.Once
// extractMaterialText runs one jz.bm.extract_material_text job: the text of
// the material's PDFs that have none yet is extracted and stored, then the
// material's search document is synced. A missing database or bucket is an
// error, so River retries; a missing pdftotext is logged once and the
// document is still synced.
func (p *Plugin) extractMaterialText(ctx context.Context, args jobs.ExtractMaterialTextArgs) error {
if p.app == nil {
return errors.New("jz.bm: app is not booted")
}
db, ok := p.app.Lookup[*gorm.DB]()
if !ok || db == nil {
return errors.New("jz.bm: database is not configured")
}
bucket, ok := p.app.Lookup[*blob.Bucket]()
if !ok || bucket == nil {
return errors.New("jz.bm: the uploads bucket is not configured")
}
_, err := classes.EnsureFileTexts(ctx, db, bucket, classes.DefaultPDFText(), args.MaterialID)
switch {
case errors.Is(err, classes.ErrPDFToolMissing):
pdfToolWarning.Do(func() {
diary.For("jz.bm").Warn("pdftotext not found; attached PDFs add no search text until poppler-utils is installed")
})
case err != nil:
return err
}
p.materialIndex.Sync(ctx, db, args.MaterialID)
return nil
}