Skip to content

feat(thumbnails): Tika-based audio cover art extraction - #3362

Closed
dschmidt wants to merge 22 commits into
feat/thumbnails-raw-embedded-previewfrom
feat/thumbnails-audio-tika
Closed

feat(thumbnails): Tika-based audio cover art extraction#3362
dschmidt wants to merge 22 commits into
feat/thumbnails-raw-embedded-previewfrom
feat/thumbnails-audio-tika

Conversation

@dschmidt

@dschmidt dschmidt commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Stacked on #3332, which lands the shared Tika unpack helper and the generic ErrNoEmbeddedImage.

Route audio cover art through Tika, so all embedded-media extraction can eventually run through Tika (and dhowden/tag be dropped).

When a Tika server is configured, cover art is extracted via Tika by default: it selects the tagged front cover (ID3 APIC "Cover (front)") via /unpack/all sidecar metadata, else the first embedded image, matching the deterministic selection from #3208. Verified byte-identical to the in-process reader on real files (front-cover and untyped). Without a Tika server it stays in-process.

THUMBNAILS_PREPROCESSOR_AUDIO_PROCESSOR=builtin forces the in-process extractor even when a Tika server is configured; that builtin path is deprecated and slated for removal in the next major.

TikaDecoder picks by mime type: audio/* front cover, everything else the largest renderable preview.

Rename RawImageDecoder to RawTiffDecoder and qualify candidates by their
actual stream instead of trusting the tags: CR2 keeps its full-size JPEG
as an IFD0 strip, DNG stores the raw sensor payload as lossless JPEG
(SOF3) which starts with a regular SOI. A JPEG segment walk to the SOF
marker accepts only the DCT processes common decoders render.
Cap the IFD queue at maxIFDs so a file packed with huge SubIFD counts can no
longer grow it without bound, and walk the embedded JPEG by a bounded number
of header segments so a preview whose SOF sits past 64KB of leading metadata
is still found instead of being scanned only within a fixed byte window.
Both are TIFF-based camera raw formats handled by the same generic walker;
NRW detection needs the reva mimetype mapping in opencloud-eu/reva#773, SRF is
already mapped. No sample files on hand for these two, added by format
structure alongside their verified siblings.
Reject a preview whose declared length exceeds 100MB rather than serving it;
previews are camera-generated JPEGs, so this bounds the output independently
of the input file size.
…ogging

Add big-endian, largest-candidate, orientation-direction, isRenderableJPEG
classification and maxPreviewLength tests plus a ForType raw-dispatch check;
log a raw file without an embedded preview at debug instead of error; name the
TIFF magic constant and de-enumerate the decoder doc comment.
… width

Review of #3332 surfaced two BigTIFF-only defects in the embedded-preview
walker (attacker-controlled input):

- 64-bit IFD and SubIFD-array offsets were bounds-checked with `off + n > len`,
  which wraps for a crafted offset near 2^64, bypassing the guard and slicing
  out of range -> panic (recovered by the framework into a 500 + stack log on
  every crafted request, defeating the no-panic goal). Now overflow-safe.
- a tag value was always read as an 8-byte Uint64 in BigTIFF; a LONG (4-byte)
  offset in a big-endian BigTIFF was thereby shifted. Read it at its declared
  type width instead.

Adds tests for the overflow paths (assert ErrNoImageFromRawFile, no panic),
the big-endian LONG offset, and strengthens the truncation test to assert the
error. Trims a few over-long comments.
isLongType accepted only LONG/LONG8, so a SubIFDs entry using the dedicated
IFD (13) or, in BigTIFF, IFD8 (18) pointer type (both standards-compliant and
common) was skipped and its embedded preview never discovered. Accept those
pointer types for the SubIFDs tag via isSubIFDType, keeping the stricter
isLongType on length fields so they never accept a pointer type. IFD8 reads at
8-byte width like LONG8.
Replace the in-process TIFF/BigTIFF walker (rawtiff.go) with a Tika-based
extractor: raw images are sent to a Tika server's /unpack endpoint, which
returns the embedded JPEG previews (Tika's RawTiffParser emits them as embedded
documents). The largest renderable JPEG is decoded through the image pipeline.

This aligns preview extraction with the search service (same Tika, same format
coverage) and drops a large hand-rolled parser. Raw thumbnails now require
THUMBNAILS_TIKA_TIKA_URL; without it, raw types fall back to the default decoder
(no thumbnail).

Audio artwork extraction stays in process for now and moves in a follow-up.
Tika sniffs TIFF-based raws (NEF/DNG/ARW/PEF) as generic image/tiff by
content; only the filename extension routes them to the raw parser. Thread
the source name through to the unpack request via Content-Disposition.
… Convert interface

The Tika preview extractor does a network call; it was using context.Background().
Pass the request context from the gRPC handler through Convert so the unpack call
respects cancellation and deadlines. Non-I/O decoders ignore the context.
@dschmidt dschmidt added Type:Maintenance E.g. technical debt, packaging, etc. Type:Enhancement labels Aug 19, 2026
@codacy-production

codacy-production Bot commented Aug 19, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 26 complexity · -28 duplication

Metric Results
Complexity 26
Duplication -28

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

… no-image error

Extract tikaUnpack + readZipEntry, rename tikaExtractPreview to tikaLargestPreview,
and rename ErrNoImageFromRawFile to the generic ErrNoEmbeddedImage. No behaviour
change; sets up reuse for audio cover art.
@dschmidt dschmidt changed the title feat(thumbnails): optional Tika-based audio cover art extraction feat(thumbnails): Tika-based audio cover art extraction Aug 19, 2026
@dschmidt
dschmidt force-pushed the feat/thumbnails-audio-tika branch 2 times, most recently from b1cbc52 to 23fd802 Compare August 19, 2026 17:51
@dschmidt

Copy link
Copy Markdown
Contributor Author

If we agree on the Tika approach, this supersedes #3208 :)

When a Tika server is configured, audio cover art is extracted through it by
default, selecting the tagged front cover (ID3 APIC 'Cover (front)') via
/unpack/all sidecar metadata, else the first embedded image. TikaDecoder picks
by mime type: audio/* front cover, everything else the largest preview.

THUMBNAILS_PREPROCESSOR_AUDIO_PROCESSOR=builtin forces the in-process extractor;
it is deprecated and slated for removal in the next major.
@dschmidt
dschmidt force-pushed the feat/thumbnails-audio-tika branch from 23fd802 to 1592ab2 Compare August 19, 2026 17:55
type Preprocessor struct {
Tika Tika `yaml:"tika"`
// AudioProcessor selects the audio cover-art extractor.
AudioProcessor string `yaml:"audio_processor" env:"THUMBNAILS_PREPROCESSOR_AUDIO_PROCESSOR" desc:"The processor for extracting cover art from audio files. When a Tika server is configured it is used by default. Set this to 'builtin' to force the in-process extractor even when a Tika server is configured. The 'builtin' processor is deprecated and will be removed in a future major release." introductionVersion:"%%NEXT%%" deprecationVersion:"%%NEXT%%" removalVersion:"%%NEXT_PRODUCTION_VERSION%%" deprecationInfo:"THUMBNAILS_PREPROCESSOR_AUDIO_PROCESSOR only selects the deprecated in-process audio extractor; once it is removed audio cover art always uses Tika when configured." deprecationReplacement:""`

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure we need this - one could argue, Tika is opt-in and when you choose to opt in, you get audio extraction via tika.

Thoughts?

@dschmidt
dschmidt force-pushed the feat/thumbnails-raw-embedded-preview branch from 4ba384f to 0d0a6a6 Compare August 30, 2026 11:47
@dschmidt

Copy link
Copy Markdown
Contributor Author

Superseded by #3332, which takes the cover art (and every other thumbnail Tika provides) through one generic Tika step on top of #3397. The branch is kept as feat/thumbnails-audio-tika-legacy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Type:Enhancement Type:Maintenance E.g. technical debt, packaging, etc.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant